简介
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常量的规定:
- 规定一:如果String.intern之前被调用过,并且返回的后果和CONSTANT_String_info中保留的编码是统一的话,示意他们指向的是同一个String的实例。
- 规定二:如果不同的话,那么会创立一个新的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/最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!
欢送关注我的公众号:「程序那些事」,懂技术,更懂你!