JVM 关于各种常量池的概念的总结和讨论以及String在JVM中的形式

各种常量池、对象、基本类型、静态变量、字符串、符号引用和直接引用的来源去向和存储地址,让人头皮发麻。所以今天就来整理一下。

这个讨论主要依据Java1.8,关于方法区中在1.8后有什么这些概念在《深入理解Java虚拟机:JVM高级特性与最佳实践》这本书都是混着讲的,讲的比较少。所以本文除了说明各种常量池都是什么,在哪,还验证方法区中还有什么?

明确需要讨论的常量池概念

名称来源去向
常量池(静态常量池)class文件类加载后存放到运行时常量池中
运行时常量池静态常量池转化
符号引用转的直接引用
方法区(元空间)
符号引用转的直接引用
方法区(元空间)
局部变量表栈空间(栈帧的一部分(局部变量表、操作数栈、动态链接、方法返回地址等)
字符串常量池堆区

常量池表(静态常量池)

静态常量池是相对于运行时常量池来说的,属于描述class文件结构的一部分。

常量池中主要存放两大类常量:字面量(Literal)和符号引用(Symbolic References)

常量池表来源于Java编译后的class文件,最终在类加载后存放到方法区的运行时常量池中。单单来讲,既然转化为运行时常量池了,class常量池表这个概念在运行时就不存在了。

运行时常量池(Runtime Constant Pool)

运行时常量池(Runtime Constant Pool)是方法区的一部分。编译期生成的Class文件中常量池表(Constant Pool Table)存放的各种字面量与符号引用将在类加载后存放到方法区的运行时常量池中。

一般除了保存Class文件中描述的符号引用外,还会把由符号引用翻译出来的直接引用也存储在运行时常量池中。

字符串常量池是JVM 实例全局共享的全局只有一个,而运行时常量池每个类都有一个。

方法区的主要职责是用于存放类型的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等。但在JDK 7的HotSpot,已经把原本放在永久代的字符串常量池、静态变量等移出。所以运行时常量池还剩什么?

存放类型的相关信息,如类名、访问修饰符、符号引用直接引用、字段描述、方法描述等。

局部变量表

每一个栈帧种都会有一个局部变量表,用来存储方法调用过程中的局部变量。

字符串常量池(stringTable维护)和静态变量

相关概念:1.7之前的永久代,1.7永久代关于字符串常量池和静态变量的变化,1.8的元空间

到了JDK 7的HotSpot,已经把原本放在永久代的字符串常量池、静态变量等移出,而到了JDK 8,终于完全废弃了永久代的概念,改用与JRockit、J9一样在本地内存中实现的元空间(Metaspace)来代替,把JDK 7中永久代还剩余的内容(主要是类型信息)全部移到元空间中。

是因为自JDK 7起,原本存放在永久代的字符串常量池被移至Java堆之中。

深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)周志明

String在JVM中的形式

字符字面量,String,StringBuffer,StringBuilder

字符字面量是Java代码中编写的,会直接在JVM根据不同情况进行处理。

String被static修饰,不可被继承。其vlaue数组被final 修饰,不可变更引用地址,又被 private修饰,除了String内部外部无法访问value,所以也修改不了value数组中的值,所以String是不可更改的,每次只会生成一个新的String。

StringBuffer和StringBuilder都继承自AbstractStringBuilder,在append操作过程中,如果长度超过value数组,就会通过Arrays.copy对其进行扩容。StringBuffer和StringBuilder的最大区别是StringBuffer 是线程安全的,其在方法上加了synchronized进行线程同步。

String str = “abc”

JAVA代码中的”abc”中表示的就是字符字面量,在JAVA加载类的过程中会直接进入字符串常量池中。

String str = “abc” + “bcd”

两个字面量相加JVM会直接把结果存入到字符串常量池中,中间的结果比如”abc”和”bcd”是不会放入到字符串常量池中的。str直接引用字符串常量池中的对象.

String str = “” + str

JDK5之前jvm编译时会转成StringBuffer的append操作,最后使用toString()方法获得String对象,所以操作后的String对象是不存在于字符串常量池中的。

JDK5及之后的版本会转为StringBuilder的append操作。

string intern方法

intern用来返回常量池中的某字符串,如果常量池中已经存在该字符串,则直接返回常量池中该对象的引用。否则,在常量池中加入该对象,然后 返回引用。

上一页 -