JVM详解之:运行时常量池

本文涉及的产品
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
简介: JVM详解之:运行时常量池

目录



简介



JVM在运行的时候会对class文件进行加载,链接和初始化的过程。class文件中定义的常量池在JVM加载之后会发生什么神奇的变化呢?快来看一看吧。


class文件中的常量池



之前我们在讲class文件的结构时,提到了每个class文件都有一个常量池,常量池中存了些什么东西呢?


字符串常量,类和接口名字,字段名,和其他一些在class中引用的常量。


运行时常量池



但是只有class文件中的常量池肯定是不够的,因为我们需要在JVM中运行起来。


这时候就需要一个运行时常量池,为JVM的运行服务。


运行时常量池和class文件的常量池是一一对应的,它就是class文件的常量池来构建的。

运行时常量池中有两种类型,分别是symbolic references符号引用和static constants静态常量。


其中静态常量不需要后续解析,而符号引用需要进一步进行解析处理。


什么是静态常量,什么是符号引用呢? 我们举个直观的例子。


String site="www.flydean.com"


上面的字符串"www.flydean.com"可以看做是一个静态常量,因为它是不会变化的,是什么样的就展示什么样的。


而上面的字符串的名字“site”就是符号引用,需要在运行期间进行解析,为什么呢?


因为site的值是可以变化的,我们不能在第一时间确定其真正的值,需要在动态运行中进行解析。


静态常量详解



运行时常量池中的静态常量是从class文件中的constant_pool构建的。可以分为两部分:String常量和数字常量。


String常量



String常量是对String对象的引用,是从class中的CONSTANT_String_info结构体构建的:


CONSTANT_String_info {
    u1 tag;
    u2 string_index;
}


tag就是结构体的标记,string_index是string在class常量池的index。


string_index对应的class常量池的内容是一个CONSTANT_Utf8_info结构体。


CONSTANT_Utf8_info {
    u1 tag;
    u2 length;
    u1 bytes[length];
}


CONSTANT_Utf8_info是啥呢?它就是要创建的String对象的变种UTF-8编码。


我们知道unicode的范围是从0x0000 至 0x10FFFF。


变种UTF-8就是将unicode进行编码的方式。那是怎么编码呢?


image.png


从上图可以看到,不同的unicode范围使用的是不同的编码方式。


注意,如果一个字符占用多个字节,那么在class文件中使用的是 big-endian 大端优先的排列方式。


如果字符范围在FFFF之后,那么使用的是2个3字节的格式的组合。


讲完class文件中CONSTANT_String_info的结构之后,我们再来看看从CONSTANT_String_info创建运行时String常量的规则:


  1. 规则一:如果String.intern之前被调用过,并且返回的结果和CONSTANT_String_info中保存的编码是一致的话,表示他们指向的是同一个String的实例。
  2. 规则二:如果不同的话,那么会创建一个新的String实例,并将运行时String常量指向该String的实例。最后会在这个String实例上调用String的intern方法。调用intern方法主要是将这个String实例加入字符串常量池。


数字常量



数字常量是从class文件中的CONSTANT_Integer_info, CONSTANT_Float_info, CONSTANT_Long_info和 CONSTANT_Double_info 构建的。


符号引用详解



符号引用也是从class中的constant_pool中构建的。


对class和interface的符号引用来自于CONSTANT_Class_info。


对class和interface中字段的引用来自于CONSTANT_Fieldref_info。


class中方法的引用来自于CONSTANT_Methodref_info。


interface中方法的引用来自于CONSTANT_InterfaceMethodref_info。


对方法句柄的引用来自于CONSTANT_MethodHandle_info。


对方法类型的引用来自于CONSTANT_MethodType_info。


对动态计算常量的符号引用来自于CONSTANT_MethodType_info。


对动态计算的call site的引用来自于CONSTANT_InvokeDynamic_info。


String Pool字符串常量池



我们在讲到运行时常量池的时候,有提到String常量是对String对象的引用。那么这些创建的String对象是放在什么地方呢?


没错,就是String Pool字符串常量池。


这个String Pool在每个JVM中都只会维护一份。是所有的类共享的。


String Pool是在1.6之前是存放在方法区的。在1.8之后被放到了java heap中。


注意,String Pool中存放的是字符串的实例,也就是用双引号引起来的字符串。


那么问题来了?


String name = new String("www.flydean.com");


到底创建了多少个对象呢?


总结



class文件中常量池保存的是字符串常量,类和接口名字,字段名,和其他一些在class中引用的常量。每个class都有一份。


运行时常量池保存的是从class文件常量池构建的静态常量引用和符号引用。每个class都有一份。


字符串常量池保存的是“字符”的实例,供运行时常量池引用。


相关文章
|
11天前
|
Java
JVM运行时数据区
1)虚拟机栈:每次调用方法都会在虚拟机栈中产生一个栈帧,每个栈帧中都有方法的参数、局部变量、方法出口等信息,方法执行完毕后释放栈帧 (2)本地方法栈:为native修饰的本地方法提供的空间,在HotSpot中与虚拟机合二为一
16 2
|
17天前
|
Java
JVM运行时数据区(内存结构)
1)虚拟机栈:每次调用方法都会在虚拟机栈中产生一个栈帧,每个栈帧中都有方法的参数、局部变量、方法出口等信息,方法执行完毕后释放栈帧 (2)本地方法栈:为native修饰的本地方法提供的空间,在HotSpot中与虚拟机合二为一 (3)程序计数器:保存指令执行的地址,方便线程切回后能继续执行代码
18 3
|
2月前
|
Java
jvm复习,深入理解java虚拟机一:运行时数据区域
这篇文章深入探讨了Java虚拟机的运行时数据区域,包括程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区、元空间和运行时常量池,并讨论了它们的作用、特点以及与垃圾回收的关系。
69 19
jvm复习,深入理解java虚拟机一:运行时数据区域
|
2月前
|
存储 SQL 小程序
JVM知识体系学习五:Java Runtime Data Area and JVM Instruction (java运行时数据区域和java指令(大约200多条,这里就将一些简单的指令和学习))
这篇文章详细介绍了Java虚拟机(JVM)的运行时数据区域和JVM指令集,包括程序计数器、虚拟机栈、本地方法栈、直接内存、方法区和堆,以及栈帧的组成部分和执行流程。
36 2
JVM知识体系学习五:Java Runtime Data Area and JVM Instruction (java运行时数据区域和java指令(大约200多条,这里就将一些简单的指令和学习))
|
2月前
|
Arthas 监控 Java
JVM知识体系学习七:了解JVM常用命令行参数、GC日志详解、调优三大方面(JVM规划和预调优、优化JVM环境、JVM运行出现的各种问题)、Arthas
这篇文章全面介绍了JVM的命令行参数、GC日志分析以及性能调优的各个方面,包括监控工具使用和实际案例分析。
59 3
|
4月前
|
存储 Java 程序员
JVM自动内存管理之运行时内存区
这篇文章详细解释了JVM运行时数据区的各个组成部分及其作用,有助于理解Java程序运行时的内存布局和管理机制。
JVM自动内存管理之运行时内存区
|
4月前
|
存储 算法 Java
JVM组成结构详解:类加载、运行时数据区、执行引擎与垃圾收集器的协同工作
【8月更文挑战第25天】Java虚拟机(JVM)是Java平台的核心,它使Java程序能在任何支持JVM的平台上运行。JVM包含复杂的结构,如类加载子系统、运行时数据区、执行引擎、本地库接口和垃圾收集器。例如,当运行含有第三方库的程序时,类加载子系统会加载必要的.class文件;运行时数据区管理程序数据,如对象实例存储在堆中;执行引擎执行字节码;本地库接口允许Java调用本地应用程序;垃圾收集器则负责清理不再使用的对象,防止内存泄漏。这些组件协同工作,确保了Java程序的高效运行。
32 3
|
4月前
|
消息中间件 设计模式 安全
多线程魔法:揭秘一个JVM中如何同时运行多个消费者
【8月更文挑战第22天】在Java虚拟机(JVM)中探索多消费者模式,此模式解耦生产与消费过程,提升系统性能。通过`ExecutorService`和`BlockingQueue`构建含2个生产者及4个消费者的系统,实现实时消息处理。多消费者模式虽增强处理能力,但也引入线程安全与资源竞争等挑战,需谨慎设计以确保高效稳定运行。
98 2
|
4月前
|
C# UED 开发者
WPF动画大揭秘:掌握动画技巧,让你的界面动起来,告别枯燥与乏味!
【8月更文挑战第31天】在WPF应用开发中,动画能显著提升用户体验,使其更加生动有趣。本文将介绍WPF动画的基础知识和实现方法,包括平移、缩放、旋转等常见类型,并通过示例代码展示如何使用`DoubleAnimation`创建平移动画。此外,还将介绍动画触发器的使用,帮助开发者更好地控制动画效果,提升应用的吸引力。
205 0
|
4月前
|
算法 Java
JVM常用运行时参数说明
JVM常用运行时参数说明