《Java 面试题系列》:一个长常识又很有意思的专栏。深刻开掘、剖析源码、汇总原理、图文联合,打造公众号系列文章,面试与否均可晋升 Level。欢送继续关注【程序新视界】。本篇为第 5 篇。
【番外篇】本篇外围:JDK 各个版本中 JDK 的运行时常量池、字符串常量池、动态常量池的性能及存储地位。
在写本系列文章时,发现一旦查究起底层实现都会波及到一些内存构造的问题。其中波及比拟多的便是常量池,本篇文章汇总一下 JDK 的运行时常量池、字符串常量池、动态常量池的性能及存储构造。
JVM 运行时内存构造
在理解常量池之前咱们先通过一张图理解一下 JVM 的整个内存分布图。下图为 JDK7 的内存构造:
在上图中 JVM 所治理的内存次要包含以下区域:程序计数器(Program Counter Register)、虚拟机栈(VM Stack)、本地办法栈(Native Method Stack)、办法区(Method Area)、堆(Heap)。
不同版本的 JVM 的内存构造有不同的变动,这些变动对咱们明天要讲的三个概念会有所影响,前面咱们会逐个解说。
理解了 JVM 内存构造,那么运行时常量池、字符串常量池、动态常量池对于的都位于 JVM 的什么区域呢?先来看看它们的定义及性能。
动态常量池
Java 程序要运行时,须要编译器先将源代码文件编译成字节码(.class)文件,而后在由 JVM 解释执行。
class 文件中除了有类的版本、字段、办法、接口等形容信息外, 还有一项信息是常量池(Constant pool table),用于寄存编译期生成的各种字面量和符号援用,这部分内容将在类加载后进入运行时常量池中寄存。
动态常量池就是下面说的 class 文件中的常量池。class 常量池是在编译时每个 class 文件中都存在。不同的符号信息搁置在不同标记的常量表中。
常量池中寄存的符号信息,在 JVM 执行指令时须要依赖应用。常量池中的所有项都具备如下通用格局:
cp_info {
u1 tag; // 示意 cp_info 的单字节标记位
u1 info[]; // 两个或更多的字节示意这个常量的信息,信息格式由 tag 的值确定}
反对的类型信息如下:
以 CONSTANT_Class 为例,它用于示意类或者接口,格局如下:
CONSTANT_Class_info {u1 tag; // 这个值为 CONSTANT_Class (7)
u2 name_index;// 一个 index,示意一个索引,援用的是 CONSTANT_UTF8_info
}
CONSTANT_Class_info 类型是由一个 tag 和一个 name_index 组成。name_index 中的 index 示意它是一个索引,援用的是 CONSTANT_UTF8_info。
CONSTANT_Utf8_info 用于示意字符常量的值,构造如下所示:
CONSTANT_Utf8_info {
u1 tag;
u2 length;
u1 bytes[length];
}
tag 示意为:CONSTANT_Utf8(1);length 指明了 bytes[]数组的长度;bytes[]数组援用了上一个 length 作为其长度。字符常量采纳改良过的 UTF- 8 编码表示。
对于动态常量池咱们须要晓得它存在于编译器,如果说与运行时无关的话,能够说运行时中的常量是 JVM 加载 class 文件之后进行调配的。
运行时常量池
运行时常量池就是将编译后的类信息放入办法区中,也就是说它是办法区的一部分。
运行时常量池用来动静获取类信息,包含:class 文件元信息形容、编译后的代码数据、援用类型数据、类文件常量池等。
运行时常量池是在类加载实现之后,将每个 class 常量池中的符号援用值转存到运行时常量池中。每个 class 都有一个运行时常量池,类在解析之后将符号援用替换成间接援用,与全局常量池中的援用值保持一致。
运行时常量池绝对于 class 文件常量池的另外一个个性是具备动态性,java 语言并不要求常量肯定只有编译器才产生,也就是并非预置入 class 文件中常量池的内容能力进入办法区运行时常量池,运行期间也可能将新的常量放入池中。
字符串常量池
字符串池里的内容是在类加载实现,通过验证、筹备阶段之后寄存在字符串常量池中。对于字符串常量池的具体实现咱们这里先不开展,前面用专门的文章来进行解说。
字符串常量池的解决机制咱们后面文章曾经讲到,只会存储一份,被所有的类共享。根本流程是:创立字符串之前查看常量池中是否存在,如果存在则获取其援用,如果不存在则创立并存入,返回新对象援用。
字符串常量池随着 JDK 版本的演变所在的地位也在一直的变动,上面咱们会专门用图解说一下。
常量池内存地位演变
在 JDK1.7 之前运行时常量池逻辑蕴含字符串常量池寄存在办法区, 此时 hotspot 虚拟机对办法区的实现为永恒代。
在 JDK1.7 字符串常量池和动态变量被从办法区拿到了堆中,运行时常量池剩下的还在办法区, 也就是 hotspot 中的永恒代。
在 JDK8 hotspot 移除了永恒代用元空间 (Metaspace) 取而代之, 这时候字符串常量池还在堆,运行时常量池还在办法区,只不过办法区的实现从永恒代变成了元空间(Metaspace)
通过下面的图解,咱们能够轻易得悉在不同的版本中办法区及外部组成部分是在一直变动的。
小结
通过本篇文章咱们针对 JDK 的运行时常量池、字符串常量池、动态常量池进行逐个解说,同时针对不同版本的 JVM(hotspot 虚拟机)中它们所处的内存地位进行了图解。
总结一下就是:动态变量处于编译器,存在于 class 文件内,可通过 javap verbose 命令查看字符串合并时查看的是动态常量池外面的内容;字符串常量池已经属于运行时常量池的一部分,位于办法区,但随着 JVM 版本的演变,二者曾经离开。在 JDK8 当前字符串常量池位于堆中,而运行时常量池位于办法区。
其实对于此局部的内容还有很多,特地是字符串常量池,欢送大家继续关注。后续会对字符串常量池再进行针对性的剖析。在此过程中也发现很多文章在没有指定 JDK 版本的状况下进行形容,都有失偏驳。本系列为大家考据,去伪存真。
参考文章:
https://blog.csdn.net/qq_3161…
https://blog.csdn.net/weixin_…
<center>程序新视界:精彩和成长都不容错过 </center>