5.3 常量池和运行时常量池
无论是在永久代或者元空间的时期,常量池和运行时常量池都是方法区的一部分。
-
什么是常量池
- 每个类的Class文件中包含了一项信息叫做常量池表。该表在编译阶段生成,包含了各种字面量常量(如字符串、数字等)、符号引用(如类和接口的全限定名、字段和方法的名称和描述符)以及一些特定的符号信息。
-
常量池的作用
-
用于存储常量数据和符号的引用,供编译器和解释器使用。
-
节省存储空间和提高执行效率,通过引用指向对应的常量数据。在避免出现重复存储相同的常量,只需要在常量池中保存一份,其他所有都是通过引用指向这一份数据。既节省空间又方便维护。
编译器和解释器是将高级语言代码转化为机器代码或解释执行的工具,它们在软件开发和执行过程中起到了关键的作用。
-
-
什么是运行时常量池
- 和常量池类似,存储类加载后运行的常量数据和符号的引用,供JVM快速访问这些常量的能力。
- 当类被加载时,它的Class文件各种的常量池信息就会放到运行时常量池当中,并把里面的符号地址变为真实地址。
- 提供快速访问常量的能力,JVM可以通过索引快速定位到具体的常量值。
- 为动态链接、动态调用、方法调用等提供支持,存储了类、方法、字段的符号引用。
-
为什么运行时常量池需要将常量池的符号地址变为真实地址?
-
因为常量池中的符号地址是相对于类文件的偏移量,并不是真正的内存地址。当类被加载时,类文件中的常量池信息会被解析并存储到运行时常量池中。
在类加载过程中,虚拟机会将类文件中的符号地址解析成真实的内存地址。这个过程称为:符号解析。通过符号解析,虚拟机就能够找到常量池中所引用的类、方法、字段等数据在内存中的实际位置,从而能够进行相关操作。
所以说,常量池中的符号地址是相对的,需要经过符号解析转换为真实的内存地址才能进行实际的访问和操作。运行时常量池提供了这种解析和转换的机制,使程序能够正确的访问常量池中的数据。
-
-
运行时常量池的特点
- 运行时常量池和Class常量池不同,它具有动态性。不要求常量在编译期产生,可以在运行时将新的常量放入常量池当中。
- 运行时常量池受到JVM的影响,如果无法申请空间也会出现OOM。
- 每个类都有自己的运行时常量池,和其他类的运行时常量池相互独立。
-
它们的关系
-
关系:编译阶段生成的常量池引用在类加载的时候被加载到运行时常量池中。
- 运行时常量池对常量池的符号引用解析(符号引用解析):在类加载过程中,解析符号引用是一个关键的步骤。符号引用包含了类、字段、方法的名称和描述符等信息,通过常量池中的符号引用可以定位到具体的类、字段或方法,并进行相应的解析和链接操作。
- 运行时常量池对常量池的类和接口的加载:常量池中存储了类和接口的全限定名信息,类加载器根据这些信息定位并加载对应的类和接口。
- 运行时常量池对常量池的字段和方法的解析:常量池中包含了字段和方法的名称、描述符等信息,通过这些信息可以在类加载过程中解析和校验字段和方法的访问权限、参数类型、返回类型等。
- 运行时常量池对常量池的字面量常量的初始化:常量池中存储了各种字面量常量的值,如字符串、整数、浮点数等,这些常量的初始化过程涉及到对常量池的访问和读取。
-
-
异同
-
常量池:
- 静态性质:编译时常量池是在编译阶段确定的,它存储了编译期间的字面量、符号引用等信息。这些信息在编译时是静态确定的,不可修改。
- 独立:每个类都有自己的常量池。每个Class文件都包含一个常量池表(Constant Pool Table),用于存储该类的常量信息
- 不可修改:编译时常量池的静态性质,编译时确定的常量值通常是不可修改的。
-
运行时常量池:
- 动态性质:运行时常量池是在类加载时被加载到内存中的,它与具体的运行环境相关。运行时常量池的目的是为了在运行时能够提供快速访问常量的机制,并支持动态性质,例如动态链接和动态调用。
- 独立与共享:每个类在内存中都有一个运行时常量池,这样可以使得每个类的常量池相互独立,实现了类之间的隔离。同时,运行时常量池也可以被多个实例共享,减少了内存占用。
- 运行时修改:运行时常量池中的常量值是可以被修改的,但是这种修改是有限制的。这种修改只能在特定的情况下进行,例如通过反射技术动态修改常量值。
-
-
总结
- 常量池是在编译阶段生成的一张表,用于存储字面量常量和符号引用。它的作用是节省存储空间、提高执行效率,并为编译器和解释器提供常量数据和符号引用的快速访问机制。运行时常量池则是在类加载到内存时,为JVM提供快速访问常量的机制。
- 常量池和运行时常量池都是引用,只不过一个代表了编译期间的引用,一个代表了运行期间的引用。
- Java虚拟机对Class文件的每一部分都有要求,但是对于运行时常量池没有任何要求,任何供应商实现的虚拟机都可以按照自己的要求实现对应的运行时常量池。
- 编译期间的常量池存储了我们在代码中定义的常量信息,而类加载时将这些常量加载到运行时常量池中,方便我们在运行时使用这些常量。
- 常量池和运行时常量池就像是店里的摩托车和买到手的摩托车。店内的摩托车你改不了,到手之后可以根据原厂基础随意改装。