简介

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进行编码的方式。那是怎么编码呢?

从上图可以看到,不同的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都有一份。

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

运行时常量池是和class或者interface一一对应的,那么如果一个class生成了两个实例对象,这两个实例对象是共享一个运行时常量池还是分别生成两个不同的常量池呢?欢迎小伙伴们留言讨论。

本文链接:http://www.flydean.com/jvm-run-time-constant-pool/

最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!

欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

JVM详解之:运行时常量池的更多相关文章

  1. 对JVM运行时常量池的一些理解

    1.JVM运行时常量池在内存的方法区中(在jdk8中,移除了方法区) 2.JVM运行时常量池中的内容主要是从各个类型的class文件的常量池中获取,对于字符串常量,可以调用intern方法人为添加,而 ...

  2. 翻译:JVM虚拟机规范1.7中的运行时常量池部分(二)

    本篇为JVM虚拟机规范1.7中的运行时常量池部分系列的第二篇. 4.4.4. The CONSTANT_Integer_info and CONSTANT_Float_info Structures ...

  3. JVM笔记3-java内存区域之运行时常量池

    1.运行时常量池属于线程共享区中的方法区. 2.运行时常量池用于编译期生成的各种自变量,符号引用,这部分内用将在类加载后接入方法区的运行时常量池中存放. 看如下代码所示,如图: public clas ...

  4. JVM 常量池、运行时常量池、字符串常量池

    常量池: 即class文件常量池,是class文件的一部分,用于保存编译时确定的数据. 保存的内容如下图: D:\java\test\out\production\test>javap -ver ...

  5. JVM-String常量池与运行时常量池

    Start with JVM 周志明先生著-<深入理解Java虚拟机>,书买回来好几天了,但是最近才准备开始搞一搞了(哭瞎…..).首先是第一章的Java以及JVM发展历史,大概知道了现行 ...

  6. String放入运行时常量池的时机与String.intern()方法解惑

    运行时常量池概述 Java运行时常量池中主要存放两大类常量:字面量和符号引用.字面量比较接近于Java语言层面的常量概念,如文本字符串.声明为final的常量值等. 而符号引用则属于编译原理方面的概念 ...

  7. 运行时常量池中的符号引用/String.intern() /ldc指令

    运行时常量池,之前放在方法区(永久代)中,1.8之后被转移到元空间,放到了native memory中. 具体的数据结构是:(看对象的内存布局,句柄访问还是对象头中保存指向类的元数据的指针,这里以对象 ...

  8. 类的加载,链接和初始化——1运行时常量池(来自于java虚拟机规范英文版本+本人的翻译和理解)

    加载(loading):通过一个特定的名字,找到类或接口的二进制表示,并通过这个二进制表示创建一个类或接口的过程. 链接:是获取类或接口并把它结合到JVM的运行时状态中,以让类或接口可以被执行 初始化 ...

  9. Class常量池、运行时常量池、字符串常量池的一些思考

    Class常量池.运行时常量池.字符串常量池 class常量池 java代码经过编译之后都成了xxx.class文件,这是java引以为傲的可移植性的基石.class文件中,在CAFEBABE.主次版 ...

随机推荐

  1. Android开发学习笔记DDMS的使用

    打开DDMS DDMS 的全称是Dalvik Debug Monitor Service,是 Android 开发环境中的Dalvik虚拟机调试监控服务. DDMS里面包含了:Device(设备) F ...

  2. html/css 滚动到元素位置,显示加载动画

    每次滚动到元素时,都显示加载动画,如何添加? 元素添加初始参数 以上图中的动画为例,添加俩个左右容器,将内容放置在容器内部. 添加初始数据,默认透明度0.左右分别移动100px. //左侧容器 .it ...

  3. Linux下如何查看硬件信息?

    我们在 Linux 下进行开发时,有时也需要知道当前的硬件信息,比如:CPU几核?使用情况?内存大小及使用情况?USB设备是否被识别?等等类似此类问题.下面良许介绍一些常用的硬件查看命令. lshw ...

  4. RabbitMQ:五、高阶

    存储机制 持久化的消息和非持久化的消息都可以被写入到磁盘. 持久化的消息一开始就会写入磁盘,如果可以,也会在内存中保存一部分以提高性能,当内存吃紧时会从内存中清楚. 非持久化的消息一般存储在内存中,内 ...

  5. C#数据结构与算法系列(二十):插入排序算法(InsertSort)

    1.介绍 插入排序算法属于内部排序算法,是对于欲排序的元素以插入的方式找寻该元素的适当位置,以达到排序的目的 2.思想 插入排序(Insertion Sorting)的基本思想是:把n个待排序的元素看 ...

  6. 痞子衡嵌入式:利用i.MXRT1xxx系列ROM提供的FlexSPI driver API可轻松IAP

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRT系列ROM中的FlexSPI驱动API实现IAP. 痞子衡的技术交流群里经常有群友提问: i.MXRT中的FlexSPI驱动 ...

  7. JDK8--06:Stream流

    一.描述 Stream流提供了筛选与切片.映射.排序.匹配与查找.归约.收集等功能 筛选与切片: filter:接收lambda,从流中排除某些元素 limit(n):截断流,使其元素不超过n ski ...

  8. 如何在 asp.net core 3.x 的 startup.cs 文件中获取注入的服务

    一.前言 从 18 年开始接触 .NET Core 开始,在私底下.工作中也开始慢慢从传统的 mvc 前后端一把梭,开始转向 web api + vue,之前自己有个半成品的 asp.net core ...

  9. .NET Core WEB API接口参数模型绑定

    .NET Core WEB API 模型绑定方式有以下表格中的几种: 特性 绑定源 [FromHeader] 请求标头 [FromQuery] 请求查询字符串参数 [FromForm] 请求正文中的表 ...

  10. 浅谈MySQL数据库

    目录 什么是数据库 定义 发展现状 数据库基本概念 数据库分类 关系数据库 非关系型数据库(NoSQL) 数据库启动与连接 启动服务端 连接数据库 用户信息查看 数据库的基本操作 表的基本操作 记录的 ...