JVM详解之运行时常量池

3次阅读

共计 2540 个字符,预计需要花费 7 分钟才能阅读完成。

简介

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/

最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!

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

正文完
 0