我们基于 OpenJDK 8 之后的版本讨论。下面给出的源码是 OpenJDK 17 ea
当我们创建一个类对象时:这个类首先会被类加载器加载,在发生类加载的时候,对应类的元数据被存入元空间。元数据分为两部分存入元空间,一部分存入了元空间的类空间另一部分存入了元空间的非类空间。如 图00 所示:
默认压缩类指针是开启的,和压缩对象指针类似(可以参考这篇文章),压缩类指针也是占用 32 位,在 64 位操作系统中,MetaWord 占用 8 字节,也就是压缩类指针最多可以管理 32G 的内存。
对象头的压缩类指针指向 MetaSpace 的类空间,类空间中存储各种指针型数据,例如实现方法多态以及 virtual call 的 vtable 与 itable 保存着方法代码地址的引用指针。非类空间中存储着比较大的元数据,例如常量池,字节码,JIT 编译后的代码等等。由于编译后的代码可能非常大,以及 JVM 对于多语言支持的扩展可能动态加载很多类,所以将 MetaSpace 的类空间与非类空间区分开。
JVM 启动参数 -XX:CompressedClassSpaceSize 指定的是压缩类空间大小,默认是 1G。-XX:MaxMetaspaceSize控制的是 MetaSpace 的总大小。
常量池存在于非类空间中。但是,感觉题主问的主要并不是Constant Pool ,而是 Symbol Table 和 String table。
对应源码:
JVM 源码中的 Constant Pool 其实是一个全局公共数组,每加载一个类,这个类元数据的指针就会加入这个数组,指针指向的实际的数据,例如字面量,符号常量等等,都会放入全局公共 Symbol Table 以及 全局公共 StringTable.运行时常量池是每个类加载之后各有一个还是全局共享的?
常量池,不论是 Constant Pool,还是Symbol Table还是String Table,都是全局共享的。题主看到的有争议的,估计说的是,每个类的元数据的引用,是在每个类独有的 Klass* 指针对象中。
2. 运行时常量池中还保存Class文件常量池中保存的字符串吗?还是说被统一划分到全局字符串常量池中?
没太明白,Class文件常量池中的字面量字符串,都会加载入 String Table 中。
3. 对于String str=new String("abc");这样的语句之所以会说创建的两个对象是因为"abc"作为字面量被保存在Class文件常量池而在类加载的连接阶段在堆中创建了对象并保存引用在字符串常量池,而new String("abc")在运行时又会在堆中开辟空间创建对象,所以说创建了两个对象这样理解对吗?
对的,很准确。
(4)Class文件常量池中的常量是在什么时候进入字符串常量池和运行时时常量池的?准备阶段还是解析阶段?
准确的来说是解析阶段,解析类文件生成 Contant Pool 以及放入 Symbol Table 还有 String Table。