在jvm中,常量池分为class文件常量池,运行时常量池,字符串常量池。
class文件常量池是Java文件经过编译后生成的class文件中的一部分,它主要包含以下图片中的内容:分为字面量和符号引用
而当类加载到内存中后,jvm就会将class常量池中的内容存放到运行时常量池中
运行时常量池是方法区的一部分。线程共享。Class文件中除了有类的版本、字段、方法、接口等信息外,还有一项信息是常量池,用于存放编译期生成的各种字面常量(编译期间就能确定的数据,存储的是实际的数据大小)和符号引用(类或接口的全限定名,字段的名称和描述符,方法的名称和描述符),这部分内容在类加载后存放到方法区的常量池中。
运行时常量池相对于class常量池一大特征就是具有动态性,java规范并不要求常量只能在运行时才产生,也就是说运行时常量池的内容并不全部来自class常量池,在运行时可以通过代码生成常量并将其放入运行时常量池中
运行时常量池除了导入class文件常量池的内容,还会保存符号引用对应的直接引用(实际内存地址)。这些直接引用是JVM在类加载之后的链接(验证、准备、解析)阶段从符号引用翻译过来的。
运行时常量池中保存的“常量”依然是字面量
和符号引用
。比如字符串,这里放的仍然是单纯的文本字符串,而不是String对象。
class文件常量池和运行时常量池中,都没有直接存储字面量对应的实际对象,比如String对象。那么String对象到底是什么时候在哪里创建的呢?
我们以下面这个简单的例子来说明使用字面量赋值方法来创建一个String对象的大致流程:
String s = "黄河之水天上来";
当Java虚拟机启动成功后,上面的字符串"黄河之水天上来"的字面量已经进入运行时常量池;
然后主线程开始运行,第一次执行到这条语句时,JVM会根据运行时常量池中的这个字面量去字符串常量池寻找其中是否有该字面量对应的String对象的引用。注意是引用。
如果没找到,就会去Java堆创建一个值为"黄河之水天上来"的String对象,并将该对象的引用保存到字符串常量池,然后返回该引用;如果找到了,说明之前已经有其他语句通过相同的字面量赋值创建了该String对象,直接返回引用即可。
字符串常量池,是JVM用来维护字符串实例的一个引用表。