运行时常量池与字符串常量池_字符串常量池在堆中还是方法区

运行时常量池与字符串常量池_字符串常量池在堆中还是方法区文章目录一、概念1、Class常量池(ClassConstantPool)1.1、常量池中数据项类型2、字符串池(StringPool、StringLiteralPool)2.1、参考文章:3、运行时常量池(RuntimeConstantPool)4、总结二、方法区的class文件信息,class常量池和运行时常量池的三者关系2.1、三者关系图:2.2、方法区class文…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

一、概念

1、常量池(Constant Pool)

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

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

在这里插入图片描述

字面量( Literal )就是我们所说的常量概念,如文本字符串、被声明为final的常量值等。

符号引用Symbolic References) 是一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可。

一般包括下面三类常量:

  • 类和接口的全限定名
  • 字段的名称和描述符
  • 方法的名称和描述符

描述符 是描述字段或方法的类型的字符串。
在这里插入图片描述

1.1、常量池中数据项类型

常量池的每一项常量都是一个表,一共有如下表所示的11种各不相同的表结构数据,这每个表开始的第一位都是一个字节的标志位(如下所示),代表当前这个常量属于哪种常量类型。

序号 常量池中数据项类型 类型标志 类型描述
1 CONSTANT_Utf8 1 UTF-8 编码的Unicode字符串
2 CONSTANT_Integer 3 int 类型字面值
3 CONSTANT_Float 4 float 类型字面值
4 CONSTANT_Long 5 long 类型字面值
5 CONSTANT_Double 6 double 类型字面值
6 CONSTANT_Class 7 对一个类或接口的符号引用
7 CONSTANT_String 8 String 类型字面值
8 CONSTANT_Fieldref 9 对一个字段的符号引用
9 CONSTANT_Methodref 10 对一个类中声明的方法的符号引用
10 CONSTANT_InterfaceMethodref 11 对一个接口中声明的方法的符号引用
11 CONSTANT_NameAndType 12 对一个字段 或 方法的部分符号引用

每种不同类型的常量类型具有不同的结构,具体的可以查看《深入理解java虚拟机》第六章的内容。

2、String Pool(字符串池、字符串常量池)

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

是在类加载完成,经过验证,准备阶段之后 中生成字符串对象实例,然后 将该字符串对象实例的 引用值 存到 String Pool 中

记住:String Pool 中存的是 引用值,而不是具体的实例对象,具体的实例对象是在堆中开辟的一块空间存放的。

在 HotSpot VM 里实现的 String Pool 功能的是一个 StringTable 类,它是一个哈希表,里面存的是 驻留字符( 也就是用双引号括起来的部分)的 引用(而不是驻留字符串实例本身),也就是说在堆中的某些字符串实例被这个 StringTable 引用之后就等同被赋予了”驻留字符串”的身份。这个StringTable在每个 HotSpot VM 的实例只有一份,被所有的类共享。

2.1、参考文章:

Java字符串池(String Pool)深度解析

3、Runtime Constant Pool( 运行时常量池 )

java文件被编译成class文件之后,也就是会生成我上面所说的 class常量池,那么运行时常量池又是什么时候产生的呢?

jvm在执行某个类的时候,必须经过加载、连接、初始化,而连接又包括验证、准备、解析三个阶段。而当类加载到内存中后,jvm就会将 class常量池 中的内容存放到 运行时常量池 中,由此可知,运行时常量池 也是每个类都有一个。

在上面我也说了,class常量池 中存的是字面量和符号引用,也就是说他们存的并不是对象的实例,而是对象的符号引用值。而经过解析(resolve)之后,也就是把符号引用替换为直接引用,解析的过程会去查询 字符串常量池 ,也就是我们上面所说的StringTable,以保证 运行时常量池所 引用的字符串与 字符串常量池 中所引用的是一致的。

举个实例来说明一下:

public class StrTest { 
   

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

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

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

  2. 然后,运行第二句的时候会生成两个实例,一个是 def 的实例对象,并且 StringTable中存储一个 def 的引用值,还有一个是new出来的一个 def 的实例对象 。

  3. 与上面那个是不同的实例,当在解析str3的时候查找StringTable,里面有 abc 的全局驻留字符串引用,所以str3的引用地址与之前的那个已存在的相同 。

  4. str4是在运行的时候调用 intern() 函数,返回StringTable中 def 的引用值,如果没有就将str2的引用值添加进去,在这里,StringTable中已经有了 def 的引用值了,所以返回上面在new str2的时候添加到StringTable中的 def 引用值,

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

这样析之后,下面三个打印的值就容易理解了。

上面程序,

  1. 首先,经过编译之后,在该类的 class常量池 中存放一些符号引用;

  2. 然后类加载之后,将 class常量池 中存放的符号引用转存到 运行时常量池 中;

  3. 然后经过验证,准备阶段之后,在堆中生成驻留字符串的实例对象(也就是上例中str1所指向的”abc”实例对象),然后将这个对象的引用存到全局String Pool中,也就是StringTable中;

  4. 最后在解析阶段,要把运行时常量池中的符号引用替换成直接引用,那么就直接查询StringTable,保证StringTable里的引用值与运行时常量池中的引用值一致,大概整个过程就是这样了。

4、总结

  • class常量池 是在编译的时候每个class都有的,在编译阶段,存放的是常量的 符号引用
  • 字符串常量池 在每个VM中只有一份,存放的是字符串常量的 引用值
  • 运行时常量池 是在类加载完成之后,将每个class常量池 中的符号引用值转存到 运行时常量池 中,也就是说,每个class都有一个 运行时常量池 ,类在 解析阶段 ,将 符号引用 替换成 直接引用 ,与 字符串常量池 中的引用值保持一致。

二、方法区的class文件信息,class常量池和运行时常量池的三者关系

面试题:

  1. 方法区里的 class文件信息 和 class文件常量池 是个什么关系。
  2. class文件常量池 和 运行时常量池 是什么关系。

2.1、三者关系图:

在这里插入图片描述

2.2、方法区class文件信息

img

可以看到在方法区里的class文件信息包括:魔数,版本号,常量池,类,父类和接口数组,字段,方法等信息,其实类里面又包括字段和方法的信息。

下面的图表是class文件中存储的数据类型:

类型 名称 数量
u4 magic 1
u2 minor_version 1
u2 major_version 1
u2 constant_pool_count 1
cp_info constant_pool constant_pool_count – 1
u2 access_flags 1
u2 this_class 1
u2 super_class 1
u2 interfaces_count 1
u2 interfaces interfaces_count
u2 fields_count 1
field_info fields fields_count
u2 methods_count 1
method_info methods methods_count
u2 attribute_count 1
attribute_info attributes attributes_count

2.3、class常量池:

在这里插入图片描述

2.4、运行时常量池:

下面是原 java代码 :

public class TestInt { 
       
	private  String str = "hello";	
   	void printInt(){ 
   
  		System.out.println(65535);
  	}  
}

编译后得到class文件,再javap -v TestInt.class,经过反编译后得到如下信息:

img

可以看出被反编译的class文件中的内容和上面所说的是能对应上的。这就解答了class文件和class文件常量池的关系

class文件常量池和运行时常量池的关系以及区别

class文件常量池存储的是当class文件被java虚拟机加载进来后存放在方法区的一些字面量和符号引用,字面量包括字符串,基本类型的常量。

运行时常量池是当class文件被加载完成后,java虚拟机会将class文件常量池里的内容转移到运行时常量池里,在class文件常量池的符号引用有一部分是会被转变为直接引用的,比如说类的静态方法或私有方法,实例构造方法,父类方法,这是因为这些方法不能被重写其他版本,所以能在加载的时候就可以将符号引用转变为直接引用,而其他的一些方法是在这个方法被第一次调用的时候才会将符号引用转变为直接引用的。

2.5、总结:

方法区里存储着 class文件的信息 和 运行时常量池,class文件的信息 包括类信息 和 class文件常量池 。

运行时常量池里的内容除了是class文件常量池里的内容外,还将class文件常量池里的符号引用转变为直接引用,而且运行时常量池里的内容是能动态添加的。例如调用String的intern方法就能将string的值添加到String常量池中,这里String常量池是包含在运行时常量池里的,但在jdk1.8后,将String常量池放到了堆中。

下面有一篇文章写的是比较好的
http://blog.csdn.net/vegetable_bird_001/article/details/51278339
https://www.cnblogs.com/holos/p/6603379.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/164770.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • route命令的使用详解[通俗易懂]

    route命令的使用详解[通俗易懂]Linux系统的route命令用于显示和操作IP路由表(show / manipulate the IP routing table)。要实现两个不同的子网之间的通信,需要一台连接两个网络的路由器,或者同时位于两个网络的网关来实现。在Linux系统中,设置路由通常是为了解决以下问题:该Linux系统在一个局域网中,局域网中有一个网关,能够让机器访问Internet,那么就需要将这台机器的IP地址设…

  • Spring Boot 无侵入式 实现API接口统一JSON格式返回

    点击上方“全栈程序员社区”,星标公众号 重磅干货,第一时间送达 作者:小魏小魏我们去那里呀 blog.csdn.net/qq_34347620/article/details/10…

  • 使用jedis操作redis_hadoop集群如何使用

    使用jedis操作redis_hadoop集群如何使用在上一篇文章中小编为大家介绍的Redis最新版本Redis-5.0.5版本的集群环境安装。集群环境的使用不可能使用客户端命令的方式,肯定要集成到我们的代码中以实现数据缓存功能。对应Java来说Redis官网向用户推荐了Java客户端,如下图所示:在Redis官网推荐的众多Java客户端中,Jedis是一个比较简单而功能强大的客户端,它实现Redis服务的封装,提供了简单的API调用。下面小编就…

  • 试用员工转正申请表怎么填_试用期转正申请500字

    试用员工转正申请表怎么填_试用期转正申请500字附件1:XXXX试用员工转正申请表XXXX试用员工转正申请表姓名张三部门研发部职务Java工程师学历大专毕业院校及时间XX大学2012.9-2015.7专业计算机信息管理试

  • 两千块钱带来的 “希望”

    两千块钱带来的 “希望”

  • 六大排序算法:插入排序、希尔排序、选择排序、冒泡排序、堆排序、快速排序「建议收藏」

    六大排序算法:插入排序、希尔排序、选择排序、冒泡排序、堆排序、快速排序「建议收藏」文章目录:1.插入排序2.希尔排序1.插入排序步骤:1.从第一个元素开始,该元素可以认为已经被排序2.取下一个元素tem,从已排序的元素序列从后往前扫描3.如果该元素大于tem,则将该元素移到下一位4.重复步骤3,直到找到已排序元素中小于等于tem的元素5.tem插入到该元素的后面,如果已排序所有元素都大于tem,则将tem插入到下标为0的位置6.重复步骤2~5动图演示如下:思路:  在待排序的元素中,假设前n-1个元素已有序,现将第n个元素插入到前面已经排好的序列中,使得前n个

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号