文章目录

  • ⭐运行时常量池
  • 《深入理解Java虚拟机》第六章 类文件结构 中关于常量池的解释:
  • 运行时常量池与Class文件常量池的关系
  • 运行时常量池与Class文件常量池区别
  • String.intern()
  • 字符串常量池


⭐运行时常量池

运行时常量池:是方法区的一部分,存放编译器生成的各种字面量和符号引用。
原文:

运行时常量池是方法区的一部分。Class 文件中除了有类的版本、字段、方法、接口等描述信息外,还有常量池表,常量池表A用于存放编译期生成的各种字面量和符号引用,而在类加载,常量池表A中的字面量和符号引用将被放到方法去的运行时常量池当中。
补充:Class文件是在编译器产生的。Java程序–>javac编译器–>字节码文件.class

《深入理解Java虚拟机》第六章 类文件结构 中关于常量池的解释:

常量池中主要存放有两大类常量:字面量和符号引用。
(1)字面量比较接近于Java语言层面的常量概念,如文本字符串、声明为final的常量值等。上面介绍的String类型的字符串常量也就是属于字面量;
(2)符合引用属于编译原理方面的概念,包括了下面三类常量:
类和接口的全限定名、 字段的名称和描述符、 方法的名称和描述符、 被模块导出或开放的包、方法句柄和方法类型、动态调用点和动态常量。
这部分信息将在类加载完成后,存放到运行时常量池当中

运行时常量池与Class文件常量池的关系

知乎:JVM详解之:运行时常量池:https://zhuanlan.zhihu.com/p/160770086 运行时常量池和class文件的常量池是一一对应的,它就是class文件的常量池来构建的。 运行时常量池中有两种类型,分别是symbolic references符号引用和static constants静态常量。 其中静态常量不需要后续解析,而符号引用需要进一步进行解析处理。

运行时常量池与Class文件常量池区别

JVM对Class文件中每一部分的格式都有严格的要求,每一个字节用于存储那种数据都必须符合规范上的要求才会被虚拟机认可、装载和执行;但运行时常量池没有这些限制,除了保存Class文件中描述的符号引用,还会把翻译出来的直接引用也存储在运行时常量区。(第二章原文)
相较于Class文件常量池,运行时常量池更具动态性,在运行期间也可以将新的变量放入常量池中,而不是一定要在编译时确定的常量才能放入。最主要的运用便是String类的intern()方法。(第二章原文)
在方法区中,常量池有运行时常量池和Class文件常量池;但其中的内容是否完全不同,暂时还未得知

String.intern()

检查字符串常量池中是否存在String并返回池里的字符串引用;若池中不存在,则将其加入池中,并返回其引用。
这样做主要是为了避免在堆中不断地创建新的字符串对象

字符串常量池

JDK1.8版本的字符串常量池中存的是字符串对象,以及字符串常量值。 附
上常考面试题: 输出结果?创建了几个对象?
String s1 = "abc"; String s2 = "abc";System.out.println(s1== s2); 结果输出:
true
创建了1个对象。采用字面值的方式创建一个字符串时,JVM首先会去字符串常量池中查找是否存在"abc"这个对象,如果不存在,则在字符串池中创建"abc"这个对象,然后将池中"abc"这个对象的引用地址返回给"abc"对象的引用s1,这样s1会指向池中"abc"这个字符串对象;如果存在,则不创建任何对象,直接将池中"abc"这个对象的地址返回,赋给引用s2。因为s1、s2都是指向同一个字符串池中的"abc"对象,所以结果为true。

输出结果?创建了几个对象?
String s3 = new String("xyz"); String s4 = new String("xyz");System.out.println(s3==s4); 结果输出:false
创建了3个对象。采用new关键字新建一个字符串对象时,JVM首先在字符串池中查找有没有"xyz"这个字符串对象,如果有,则不在池中再去创建"xyz"这个对象了,直接在中创建一个"xyz"字符串对象,然后将堆中的这个"xyz"对象的地址返回赋给引用s3,这样,s3就指向了堆中创建的这个"xyz"字符串对象;如果没有,则首先在字符串池中创建一个"xyz"字符串对象,然后再在堆中创建一个"xyz"字符串对象,然后将堆中这个"xyz"字符串对象的地址返回赋给s3引用,这样,s3指向了堆中创建的这个"xyz"字符串对象。s4则指向了堆中创建的另一个"xyz"字符串对象。s3s4是两个指向不同对象的引用,结果当然是false。
代码详解
String s1 = new StringBuilder().append("ja").append("va1").toString();System.out.println(s1.intern() == s1); 输出结果:true
详解:StringBuilder().toString()这个方法虽然是new了一个String对象,但其实和"java1"是一样的,大家可以看下源码,这里的s1.intern()返回的是常量池中字符串的引用,所以s1.intern()
== s1。