Java中几种常量池的区分

news2025/1/11 20:50:33

文章目录

  • 前言
    • 了解一下 ldc 指令
    • 字符串常量池在 Java 内存区域的哪个位置
  • 1.全局字符串池(string pool也有叫做string literal pool)
  • 2.class文件常量池(class constant pool)
  • 3.运行时常量池(runtime constant pool)
  • 三种常量池之间的关联
  • 范例1
  • 范例2
  • 范例3
    • 注意
  • 总结
  • 参考文章


前言

总共分为三种常量池 本文是对网上的资料做的总结 下面做区分
Java 的 JVM 的内存可分为 3 个区:堆内存(heap)、栈内存(stack)和方法区(method)也叫静态存储区。

本文所有代码都是基于 JDK1.8 进行的。

在探讨常量池的类型之前需要明白什么是常量

  • 用 final 修饰的成员变量表示常量,值一旦给定就无法改变!
  • final 修饰的变量有三种:静态变量、实例变量和局部变量,分别表示三种类型的常量。

了解一下 ldc 指令

简单地说,它用于将 String 型常量值从常量池中推送至栈顶。

字符串常量池在 Java 内存区域的哪个位置

1)在 JDK6.0 及之前版本,字符串常量池是放在 Perm Gen 区(也就是方法区)中,此时常量池中存储的是对象。
2)在 JDK7.0 版本,字符串常量池被移到了堆中了。此时常量池存储的就是引用了。在 JDK8.0 中,永久代(方法区)被元空间取代了。

在 Java 的内存分配中,总共 3 种常量池:


1.全局字符串池(string pool也有叫做string literal pool)

全局字符串池里的内容是在类加载完成,经过验证,准备阶段之后在堆中生成字符串对象实例,然后将该字符串对象实例的引用值存到string pool中(记住:string pool中存的是引用值而不是具体的实例对象,具体的实例对象是在堆中开辟的一块空间存放的。)。
在HotSpot VM里实现的string pool功能的是一个StringTable类,HashSet<String>,它是一个哈希表,里面存的是驻留字符串(也就是我们常说的用双引号括起来的)的引用(而不是驻留字符串实例本身),也就是说在堆中的某些字符串实例被这个StringTable引用之后就等同被赋予了”驻留字符串”的身份。
这个StringTable在每个HotSpot VM的实例只有一份,被所有的类共享。

2.class文件常量池(class constant pool)

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

  • 字面量就是我们所说的常量概念,如文本字符串、被声明为final的常量值等。
  • 符号引用是一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可(它与直接引用区分一下,直接引用一般是指向方法区的本地指针,相对偏移量或是一个能间接定位到目标的句柄)。一般包括下面三类常量:

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

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

例如:

CONSTANT_Utf8_info{
    u1 tag;
    u2 length;
    u1 bytes[];
}

在这里插入图片描述

3.运行时常量池(runtime constant pool)

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

jvm在执行某个类的时候,必须经过加载、连接、初始化,而连接又包括验证、准备、解析三个阶段。而当类加载到内存中后,jvm就会将class常量池中的内容存放到运行时常量池中,由此可知,运行时常量池也是每个类都有一个。在上面我也说了,class常量池中存的是字面量和符号引用,也就是说他们存的并不是对象的实例,而是对象的符号引用值。而经过解析(resolve)之后,也就是把符号引用替换为直接引用,解析的过程会去查询全局字符串池,也就是我们上面所说的StringTable,以保证运行时常量池所引用的字符串与全局字符串池中所引用的是一致的。

三种常量池之间的关联

笼统的解释:在类加载阶段, JVM 会在堆中创建对应这些 class 文件常量池中的字符串对象实例,并在字符串常量池中驻留其引用。具体在 resolve 阶段执行。这些常量全局共享。

就 HotSpot VM 的实现来说,加载类的时候,那些字符串字面量会进入到当前类的运行时常量池,不会进入全局的字符串常量池(即在 StringTable 中并没有相应的引用,在堆中也没有对应的对象产生)。所以上面提到的,经过 resolve 时,会去查询全局字符串池,最后把符号引用替换为直接引用。(即字面量和符号引用虽然在类加载的时候就存入到运行时常量池,但是对于 lazy resolve 的字面量,具体操作还是会在 resolve 之后进行的。)

范例1

 public static void main(String[] args) {
        String s1 = "abc";  
        String s2 = "abc";
        String s3 = "xxx";
    }

在这里插入图片描述

String s1 = "abc";resolve 过程在字符串常量池中发现没有”abc“的引用,便在堆中新建一个”abc“的对象,并将该对象的引用存入到字符串常量池中,然后把这个引用返回给 s1。

String s2 = "abc"; resolve 过程会发现 StringTable 中已经有了”abc“对象的引用,则直接返回该引用给 s2,并不会创建任何对象。

String s3 = "xxx"; 同第一行代码一样,在堆中创建对象,并将该对象的引用存入到 StringTable,最后返回引用给 s3。

范例2

 public static void main(String[] args) {
        String s1 = "ab";//#1
        String s2 = new String(s1+"d");//#2
        s2.intern();//#3
        String s4 = "xxx";//#4
        String s3 = "abd";//#5
        System.out.println(s2 == s3);//true
    }

在这里插入图片描述进入 main 方法,对每行代码进行解读。

1,ldc 指令会把“ab”加载到栈顶,换句话说,在堆中创建“ab”对象,并把该对象的引用保存到字符串常量池中。
2,ldc 指令会把“d”加载到栈顶,然后有个拼接操作,内部是创建了一个 StringBuilder 对象,一路 append,最后调用 StringBuilder 对象的 toString 方法得到一个 String 对象(内容是 abd,注意 toString 方法会 new 一个 String 对象),并把它赋值给 s2(赋值给 s2 的依然是对象的引用而已)。注意此时没有把“abd”对象的引用放入字符串常量池。
3,intern 方法首先会去字符串常量池中查找是否有“abd”对象的引用,如果没有,则把堆中“abd”对象的引用保存到字符串常量池中,并返回该引用,但是我们并没有使用变量去接收它。
4,无意义,只是为了说明 class 文件中的“abd”字面量是#5时得到的。
5,字符串常量池中已经有“abd”对象的引用,因此直接将该引用返回给 s3。

范例3

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
  • 上面程序的首先经过编译之后,在该类的class常量池中存放一些符号引用,然后类加载之后,将class常量池中存放的符号引用转存到运行时常量池中,然后经过验证,准备阶段之后,在堆中生成驻留字符串的实例对象(也就是上例中str1所指向的”abc”实例对象),然后将这个对象的引用存到全局String Pool中,也就是StringTable中,最后在解析阶段,要把运行时常量池中的符号引用替换成直接引用,那么就直接查询StringTable,保证StringTable里的引用值与运行时常量池中的引用值一致,大概整个过程就是这样了。

  • 回到上面的那个程序,现在就很容易解释整个程序的内存分配过程了,首先,在堆中会有一个”abc”实例,全局StringTable中存放着”abc”的一个引用值,然后在运行第二句的时候会生成两个实例,一个是”def”的实例对象,并且StringTable中存储一个”def”的引用值,还有一个是new出来的一个”def”的实例对象,与上面那个是不同的实例,当在解析str3的时候查找StringTable,里面有”abc”的全局驻留字符串引用,所以str3的引用地址与之前的那个已存在的相同,str4是在运行的时候调用intern()函数,返回StringTable中”def”的引用值,如果没有就将str2的引用值添加进去,在这里,StringTable中已经有了”def”的引用值了,所以返回上面在new str2的时候添加到StringTable中的 “def”引用值,最后str5在解析的时候就也是指向存在于StringTable中的”def”的引用值,那么这样一分析之后,下面三个打印的值就容易理解了。

注意

在运行 String str2 = new String("def"); 时会生成两个实例:一个是”def”的实例对象,并且在StringTable中存储一个该”def”对象的引用值;另一个是new出的一个”def”的实例对象,与第一个是不同的实例。

总结

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

参考文章

https://zhuanlan.zhihu.com/p/107776367

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/146097.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

干货 | Python的面试题目+答案合集

作为一个 Python 新手&#xff0c;你必须熟悉基础知识。 在本期内容中我们将讨论一些 Python 面试的基础问题和高级问题以及答案&#xff0c;以帮助你完成面试。 包括 Python 开发问题、编程问题、数据结构问题、和 Python 脚本问题。 接下来让我们来深入研究这些问题 Pytho…

AD转换芯片精度计算及校正方法

文章目录前言一、转换精度二、重要参数1.线性误差&#xff08;INL&#xff09;和差分线性误差&#xff08;DNL&#xff09;2.失调误差和增益误差三、转换校正总结前言 本文对模数转换芯片的精度进行简要介绍&#xff0c;帮助大家正确选型&#xff0c;并介绍了一个基本的ADC转换…

postgresql13+postgis3.2安装教程

postgresql13postgis3.2安装教程 安装postgresql13 安装pg13 pg13安装包 cd /home/soft/pg tar -zxvf postgresql-13.6.tar.gz cd postgresql-13.6# yum -y install -y readline-devel./configure --prefix/usr/local/pgsqlmake -j4make install设置环境变量 vim /etc/pr…

OpenGPT的11种高效用法

1. 问答提示 2. 解释复杂的概念 3. 创作 创作需要尽可能的缩小范围&#xff0c;提出具体的要求&#xff0c;AI会给出更好的答案。 4. 准备面试 5. 教师教案 6. 编码和集成 7. 健身 8. 送礼推荐 9. 翻译 这个甚至不用去演示&#xff0c;openAI的翻译能力非常强大&#xff0c…

计算机专业混子应届生,如何3个月逆袭,成功上岸?

在我进入大学之前&#xff0c;我一直对计算机感兴趣。虽然只是考了一个一般大学&#xff0c;但是选专业的时候还是选了计算机专业。 本来以为自己会在大学里学到很多有用的知识&#xff0c;并且能够很快找到一份好工作。但是&#xff0c;事实并不是这样。在大学期间&#xff0c…

竞品分析:叮咚买菜

​生鲜包括了三类未加工的初级产品“果蔬&#xff08;水果蔬菜&#xff09;、肉类、水产品”以及两类加工产品“面包和熟食”。熟食又包括“冷藏的冷冻食品、乳制品和非冷藏的散装杂粮”。 生鲜电商就是以电子商务的形式销售以上产品。 生鲜电商有着“悠久”的历史&#xff0…

Vue组件的生命周期

一、生命周期 & 生命周期函数 1. 生命周期&#xff08;Life Cycle&#xff09;是指一个组件从创建 -> 运行 -> 销毁的整个阶段&#xff0c;强调的是一个时间段。 2. 生命周期函数&#xff1a;是由 vue 框架提供的内置函数&#xff0c;会伴随着组件的生命周期&#xf…

第四十三讲:神州防火墙混合模式的初始配置

混合模式即相当于防火墙既工作于路由模式&#xff0c;又工作于透明模式。在实际应用环境中&#xff0c;此类防火墙应用一般也比较广泛。混合模式分为两种&#xff1a; 一&#xff0c;ISP分配外网地址&#xff0c;内网为私网地址&#xff0c;服务器区域和内部地址为同一网段。这…

智慧商圈,对接微信、支付宝、云闪付实现自动积分

线下交易 商城下的门店使用本公司内部的pos机以及会员管理系统&#xff0c;通过pos收银时&#xff0c;根据管理系统配置的积分规则&#xff0c;可以自行给会员积分。但是当商城内部的部分门店不是使用内部的pos机进行收银时&#xff0c;则无法给给该批用户进行积分。以前有拍照…

吉他谱软件guitar pro2023吉他和弦、六线谱、BASS四线谱绘制

Guitar Pro由法国Arobas Music出品&#xff0c;主要用于管弦乐器的学习&#xff0c;通过建立不同的音轨&#xff0c;可完成不同乐器乐谱的编排制作。Guitar Pro发布23余年来&#xff0c;其强大的功能被广泛应用于专业乐队的创作和排练&#xff0c;其独创的gtp文档格式在专业领域…

「数据密集型系统搭建」原理篇|夯实基础,灵活设计

正所谓“完事开头难”&#xff0c;在设计技术方案时候&#xff0c;除了前期要做好背景调查、需求调研&#xff0c;开工动手的第一步就是做“数据建模”&#xff0c;也就是存储数据的结构设计&#xff0c;大部分时间是围绕关系型数据库进行的&#xff0c;少部分是在Redis上做K-V…

「技术直播」分布式数据库订阅功能的原理及实现

数据订阅是一种数据查询方式&#xff0c;其特点为&#xff1a;客户端执行一个查询语句后&#xff0c;可以增量形式&#xff0c;不断收到新到达服务端的、符合查询条件的数据。相比普通查询&#xff0c;订阅能够持续地、低延迟地将新写入的数据返回客户端。什么时候需要数据订阅…

40 行 Python 代码,写一个 CPU!

目录 一、引言 二、CPU 的组成 三、工作原理 四、CPU 指令工作详细剖析 五、 Python 实现 CPU 各组成部分 六、集成 CPU 七、为CPU编程&#xff0c;体会上古程序员 工作流程 八、总结 一、引言 CPU 如何工作&#xff1f;是困扰初级用户一个迷雾般的难题。我们可能知道诸…

HTML1

HTML是一门语言&#xff0c;所有的网页都是用HTML这门语言编写出来的 HTML(Hyper Text Markup Language)&#xff1a;超文本标记语言 超文本&#xff1a;超越文本的限制&#xff0c;除了文字信息&#xff0c;还可以定义图片、音频、视频等内容 标记语言&#xff1a;由标签构…

联合证券|北京宣布:全域禁放!新能源大爆发,有港股盘中暴涨超40%

周五A股商场和港股商场进一步走强&#xff0c;连续近期连续涨势&#xff0c;其间上证指数盘中再创近期反弹以来新高&#xff0c;新能源赛道股团体大涨&#xff0c;成为商场新的领涨主力。 北向资金也持续大举净买入&#xff0c;继昨天净买入超越百亿元之后&#xff0c;今天上午…

续集来了丨UI自动化测试(二):带视频,实在RPA高效进行web项目UI自动化测试

一、什么是web项目ui自动化测试&#xff1f; 通过测试工具模拟人为操控浏览器&#xff0c;使软件按照测试人员的预定计划自动执行测试的一种方式&#xff0c;可以完成许多手工测试无法完成或者不易实现的繁琐工作。 正确使用自动化测试&#xff0c;可以更全面的对软件进行测试…

让 APISpace 告诉你什么场景使用什么API(3)

Q&#xff1a;跨境电商平台&#xff0c;需要在界面上展示跨境包裹的物流信息&#xff0c;需要使用的是什么API&#xff1f; 需要 跨境国际快递物流查询API~ 跨境国际快递物流查询服务&#xff0c;支持900物流商&#xff0c;提供实时查询和单号订阅API接口。稳定高效&#xff…

160链表-相交链表

题目 链接&#xff1a;160. 相交链表 - 力扣&#xff08;LeetCode&#xff09; 思路 是指针相同&#xff0c;先求出两个链表的长度以及差值&#xff0c;让curA移动到和curB末尾对齐的位置&#xff0c;然后就可以遍历比较是否相同 代码&#xff1a; /*** Definition for sing…

【阶段二】Python数据分析数据可视化工具使用04篇:核密度估计图

本篇的思维导图: 核密度估计图 核密度估计图(kernel density plot)用于显示数据在X轴连续数据段内的分布状况。这种图表是直方图的变种,使用平滑曲线来绘制数值水平,从而得出更平滑的分布。核密度估计图比统计直方图优胜的一个地方,在于它们不受所使用分组数量的…

一文学会Linux IPC通信

目录 概览 单工、半双工、双工 各种IPC通信方式优缺点 1. 管道 2. 消息队列 3. 共享内存 4. 信号量 5. 信号 6. Socket套接字 精筛优秀博客 原本想详细写下&#xff0c;后发现有很多人总结过&#xff0c;那我就直接战在巨人的肩膀上不再重复造轮子了。 概览 单工、半…