JVM内存结构-常量池

目录

1、常量池

1.1 概念

2、字符串常量池

2.1 概念

3、运行时常量池

3.1 概念

4、示例


本文都是以JDK1.8为例

1、常量池

1.1 概念

常量池(Constant Pool),也叫 class 文件常量池(Class Constant Pool)。

java文件被编译成 class文件,class文件中除了包含类的版本、字段、方法、接口等描述信息外,还有一项就是常量池(Constant Pool),用于存放编译器生成的各类字面量( Literal )和 符号引用(Symbolic References)。

这里写图片描述

2、字符串常量池

2.1 概念

String Pool (字符串池),即 String Literal Pool , 又叫 全局字符串池 、字符串常量池

字符串池里的内容是在类加载完成,经过验证,准备阶段之后在堆中生成字符串对象实例,然后将该字符串对象实例的引用值存到string pool中(记住:string pool中存的是引用值而不是具体的实例对象,具体的实例对象是在堆中开辟的一块空间存放的。)。

在HotSpot VM里实现的string pool功能的是一个StringTable类,它是一个哈希表,里面存的是驻留字符串(也就是我们常说的用双引号括起来的)的引用(而不是驻留字符串实例本身),也就是说在堆中的某些字符串实例被这个StringTable引用之后就等同被赋予了”驻留字符串”的身份。这个StringTable在每个HotSpot VM的实例只有一份,被所有的类共享。真正的字符串实例是存放在堆内存中的(字符串池在逻辑上是属于运行时常量池的一部分)

常量池其实就是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等信息,我们可以通过 javap -v  类名.class 指令反编译一个简单的程序看到如下的常量池信息

该类被加载,它的常量池信息就会放入运行时常量池,并把里面的符号地址(上图中的#1,#2等)变为真实地址

3、运行时常量池

3.1 概念

运行时常量池(Runtime Constant Pool)是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池表(Constant Pool Table),用于存放编译期生成的各种字面量与符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中(jvm在执行某个类的时候,必须经过加载、连接、初始化,而连接又包括验证、准备、解析三个阶段。而当类加载到内存中后,jvm就会将class常量池中的内容存放到运行时常量池中,由此可知,运行时常量池也是每个类都有一个)。

Java虚拟机对于Class文件每一部分(自然也包括常量池)的格式都有严格规定,如每一个字节用于存储哪种数据都必须符合规范上的要求才会被虚拟机认可、加载和执行,但对于运行时常量池,《Java虚拟机规范》并没有做任何细节的要求,不同提供商实现的虚拟机可以按照自己的需要来实现这个内存区域,不过一般来说,除了保存Class文件中描述的符号引用外,还会把由符号引用翻译出来的直接引用也存储在运行时常量池中。

运行时常量池相对于Class文件常量池的另外一个重要特征是具备动态性,Java语言并不要求常量一定只有编译期才能产生,也就是说,并非预置入Class文件中常量池的内容才能进入方法区运行时常量池,运行期间也可以将新的常量放入池中,这种特性被开发人员利用得比较多的便是String类的intern()方法。既然运行时常量池是方法区的一部分,自然受到方法区内存的限制,当常量池无法再申请到内存时会抛出OutOfMemoryError异常

String.intern()
检查字符串常量池中是否存在String并返回池里的字符串引用;若池中不存在,则将其加入池中,并返回其引用。
这样做主要是为了避免在堆中不断地创建新的字符串对象

运行时常量池包含字符串常量池,这个包含只是功能模块上的包含意思,实际上在HotSpot虚拟机中常量池保存在元空间,而字符串常量池保存在堆区。

4、示例

public class TestCom {
	
	public static void main(String[] args) {
		String str1 = "hello";
		String str2 = new String("world");
		String str3 = "hello";
		String str4 = str2.intern();
		String str5 = "world";
		System.out.println(str1 == str3);// true
		System.out.println(str2 == str4);// false
		System.out.println(str4 == str5);// true
	}
}

解析程序的内存分配过程:

1、首先,在堆中会有一个 hello 实例对象,全局 StringTable 中存放着 hello 的一个引用值。

2、运行第二句的时候JVM会先从字符串常量池中查询是否存在"world"这个对象,

若不存在则会在常量池中创建"world"对象,同时在堆中创建"world"这个对象,然后将堆中的这个对象的地址返回赋给引用str2。

若字符串常量池存在则直接在堆中创建"world"这个对象,然后将堆中的这个对象的地址返回赋给引用str。

也就是说,无论字符串常量池中是否存在,都会在堆中创建该对象

3、str4是在运行的时候调用 intern() 函数,返回StringTable中 world的引用值,若是没有就将str2的引用值添加进去,在这里,StringTable中已经有了 world的引用值了,因此返回上面在new str2的时候添加到StringTable中的 world引用值,

最后str5在解析的时候就也是指向存在于StringTable中的 world的引用值。

附加图

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值