class常量池、运行时常量池和字符串常量池的关系

news/2024/9/20 3:56:09/ 标签: java, 开发语言, 面试

类常量池、运行时常量池和字符串常量池这三种常量池,在Java中扮演着不同但又相互关联的角色。理解它们之间的关系,有助于深入理解Java虚拟机(JVM)的内部工作机制,尤其是在类加载、内存分配和字符串处理方面。

类常量池(Class Constant Pool)

每个Java类文件(.class文件)都具有自己的类常量池,它用于存储编译期生成的常量,包括各种字面量(字面量就是指由字母、数字等构成的字符串或者数值)和符号引用(比如类和接口的全名、字段的名称和描述符、方法的名称和描述符)。类常量池在编译期间就已经被确定,并被保存在.class文件中。

Class常量池是用来保存常量的一个中间场所。在JVM真的运行时,需要把类常量池中的常量加载到内存中的运行时常量池中。

运行时常量池(Runtime Constant Pool)

运行时常量池是类被加载到JVM时类常量池的内存版本:当Java类被加载到JVM时,各个类文件中的类常量池内容被读取并存入到运行时常量池中,其中字符串的部分直接进到字符串池,其他常量进入到运行时常量池。

根据Java虚拟机规范约定:每一个运行时常量池都在Java虚拟机的方法区中分配,在加载类和接口到虚拟机后,就创建对应的运行时常量池。

规范中规定了运行时常量池属于方法区,但是没规定方法区属于哪。于是虚拟机在各自实现的时候就各显神通了。在不同版本的JDK中,运行时常量池所处的位置也不一样。以HotSpot虚拟机为例:

在JDK 1.7之前,方法区位于永久代,运行时常量池作为方法区的一部分,处于永久代中,字符串常量池位于运行时常量池的一部分也处于永久代中。

jdk1.7之间jvm内存结构
因为使用永久代实现方法区可能导致内存泄露问题,所以,从JDK1.7开始,JVM尝试解决这一问题。

在JDK 1.7中,静态变量和运行时常量池中的字符串常量池转移到了堆内存中,其他类型的常量还保留在方法区中。
在这里插入图片描述
在JDK 1.8中,彻底移除了永久代,方法区通过元空间的方式实现,元空间是使用本地内存(Native Memory)来存储类的元数据信息的。随之,运行时常量池也在元空间中实现。
在这里插入图片描述

运行时常量池中包含了若干种不同的常量,他的来源主要有两种:

  • 编译期可知的字面量和符号引用(来自Class常量池)

  • 运行期解析后可获得的常量(如String的intern方法)

字符串常量池(String Constant Pool)

字符串常量池专门用于存储字符串常量。对于 Hotspot 虚拟机来说,类加载时,字符串字面量作为类常量池的一部分信息被载入运行时常量池中,它们以特殊的形式存储在运行时常量池中,此时它们并未被实例化为Java堆中的String对象。只有当这个字符串字面量被调用时,才会对其进行解析,即检查字符串常量池中是否已经存在相同内容的字符串对象。如果存在,就直接返回指向该对象的引用,如果不存在,虚拟机会在字符串常量池中创建一个对应的String实例,并返回这个新实例的引用。

这种处理方式的优势在于,可以减少在类加载阶段对内存的需求和降低开销,因为不是所有的字符串字面量在类的使用周期内都会被用到。同时,此方法延迟了String对象的实例化,直到它们真正被需要,这有助于提高性能并减少内存的无谓占用。

为什么从JDK 1.7开始,字符串常量池从永久代中挪到堆(Heap)中?

主要原因是因为永久代的 GC 回收效率太低,只有在FulIGC的时候才会被执行回收。但是Java中往往会有很多字符串的生命周期都很短暂,将字符串常量池放到堆中,能够更高效及时地回收字符串内存。

字符串常量池中的常量有以下几个来源:
1、字面量常量。
在代码中直接使用双引号括起来的字符串字面值(如 strings="hello”)会被认为是常量,并且会在编译后进入class文件的常量池,并且在运行阶段,进入字符串常量池。这是最常见的字符串常量来源。
2、intern()方法
String类提供了一个intern方法,用于将字符串对象手动添加到字符串常量池中。调用intern()方法时,如果字符串常量池中已经存在相同内容的字符串,将会返回常量池中的引用;如果不存在,则会在常量池中添加该字符串在堆中的引用。

以下有几个非常值得我们注意的地方:

(1)字符串常量池是一个固定大小的Hashtable,默认值大小长度是1009,如果放进字符串常量池的String非常多,就会造成Hash冲突严重,从而导致链表会很长,而链表长了后直接会造成的影响就是当调用String.intern时性能会大幅下降(因为要一个一个找,以此判断字符串常量在不在字符串常量池中)。在jdk6中StringTable是固定的,就是1009的长度,所以如果常量池中的字符串过多就会导致效率下降很快。在jdk7中,StringTable的长度可以通过一个参数指定:

-XX:StringTableSize=99991

(2)对于字符串的拼接,纯字面量和字面量的拼接,会把拼接结果作为常量保存到字符串池。如果在字符串拼接中,有一个参数是非字面量,而是一个变量的话,整个拼接操作会被编译成StringBuilder.append,这种情况编译器是无法知道其确定值的。只有在运行期才能确定。
比如这段代码:

java">string s1 = "Hollis";
string s2 = "Chuang";
string s3 = s1 + s2;
string s4 = "Hollis" + "Chuang";

在经过反编译后得到代码如下:

java">String s1 = "Hollis";
string s2 = "chuang" ;
string s3 = (new stringBuilder() ).append(s1).append(s2).toString( );
String s4 = "Hollischuang";

(3)jdk7版本在修改了常量池的基础上,也对intern函数做了修改:
在jdk7之前,字符串常量池位于永久代中,使用intern函数,如果字符串常量池中没有该字符串对象,会在字符串常量池中创建一个该对象,但是在jdk7及之后,字符串常量池移到了堆中,使用intern函数,如果字符串常量池中没有该字符串对象,则不会在字符串常量池中创建对象,而是保存堆中该对象的引用。 比如:

java">String s = new String("hello") + new String("world");
s.intern();

在第一句代码中,我们创建了一个引用变量s,其指向堆中字符串对象"helloworld",而后调用intern函数,此时字符串常量池中没有"helloworld"字符串对象,如果是在jdk7之前,jvm会在位于永久代的字符串常量池中创建一个"helloworld"字符串对象,但是jdk7之后,字符串常量池位于堆中,不再需要重新创建字符对象,而是在字符串常量池中保存堆中"helloworld"对象的引用。

关于intern函数可以学习这篇文章,讲解非常通透:深入解析String#intern


http://www.ppmy.cn/news/1460965.html

相关文章

linux程序分析命令(三)

linux程序分析命令(三) **ldd:**用于打印共享库依赖。这个命令会显示出一个可执行文件所依赖的所有共享库(动态链接库),这对于解决运行时库依赖问题非常有用。**nm:**用于列出对象文件的符号表。这个命令可以显示出定…

【vector】迭代器

Vector的基本数据结构 可以看到end指向的是数组的最后一个元素&#xff1b; 那么在使用函数遍历的时候就要注意这种清理&#xff1b; 比如计算一个数组前5个数字的最小值&#xff1b; vector<int> prices{2,1,4,2,0,52,12};auto iter_min min_element(prices.begin(),pr…

对云原生整体解决方案的进一步复盘

云原生当前虽然越来越受到传统企业的重视&#xff0c;但是由于很多企业本身信息化建设水平相对滞后&#xff0c;因此当前云原生涉及到的微服务&#xff0c;容器&#xff0c;DevOps等管理和技术实践更多也集中在金融&#xff0c;电信&#xff0c;能源&#xff0c;互联网等企业偏…

04、 .java程序用 editplus 工具打开的过程及在 editplus 工具中配置 java/javac 命令的过程

EditPlus 工具的使用&#xff1a; 1、安装 editplus 工具的过程&#xff1a;其一、安装包地址&#xff1a;其二、安装步骤&#xff1a; 2、使用 editplus 工具打开 .java 程序的过程&#xff1a;其一、修改默认打开 .java 的工具&#xff1a;其二、效果展示&#xff1a; 3、在 …

Java入门基础学习笔记14——数据类型转换

类型转换&#xff1a; 1、存在某种类型的变量赋值给另一种类型的变量&#xff1b; 2、存在不同类型的数据一起运算。 自动类型转换&#xff1a; 类型范围小的变量&#xff0c;可以直接赋值给类型范围大的变量。 byte类型赋值给int类型&#xff0c;就是自动类型转换。 pack…

采用java+B/S开发的全套医院绩效考核系统源码springboot+mybaits 医院绩效考核系统优势

采用java开发的全套医院绩效考核系统源码springbootmybaits 医院绩效考核系统优势 医院绩效管理系统解决方案紧扣新医改形势下医院绩效管理的要求&#xff0c;以“工作量为基础的考核方案”为核心思想&#xff0c;结合患者满意度、服务质量、技术难度、工作效率、医德医风等管…

Cocos Creator 3.8.x 透明带滚动功能的容器

ScrollView 是一种带滚动功能的容器 1、删除ScrollView下Sprite组件的SpriteFrame 2、ScrollView下scrollBar的Sprite组件的Color设为&#xff1a;FFFFFF00 3、ScrollView下view的Graphics组件的FillColor设为&#xff1a;FFFFFF00

sass详解

Sass&#xff08;Syntactically Awesome Style Sheets&#xff09;是一种CSS预处理器&#xff0c;它增加了许多额外的功能和功能来改进CSS的编写和维护过程。 以下是Sass的一些主要功能&#xff1a; 1. 变量&#xff1a;Sass允许你声明并使用变量来存储常用的值&#xff0c;这…

结合场景,浅谈深浅度拷贝

有两段代码是这样的&#xff1a; A段&#xff1a; List<String> list1 new ArrayList<>(); Bear B new Bear(); for(Apple apple : apples){B.url apple.url;B.content apple.content;list1.add(Bear); } B段&#xff1a; List<String> list1 new A…

做跨境电商如何解决IP独立环境?

在跨境电商领域&#xff0c;确保独立IP环境对于保护商家信息安全、避免账号关联风险以及提升操作效率至关重要。以下是解决跨境电商IP独立环境的几种方法&#xff1a; 使用虚拟专用网络 虚拟专用网络是一种可以在公共网络上建立加密通道的技术&#xff0c;通过这种技术可以使…

C++Primer Plus第五章结构编程练习10

10.编写一个使用嵌套循环的程序&#xff0c;要求用户输入一个值&#xff0c;指出要显示多少行。然后&#xff0c;程序将显示相应行数的星号&#xff0c;其中第一行包括一个星号&#xff0c;第二行包括两个星号&#xff0c;依此类推。每一行包含的字符数等于用户指定的行数&…

【计算机网络】数据链路层 组帧 习题4

组帧 发送方根据一定的规则将网络层递交的分组封装成帧(也称为组帧)。 组帧时&#xff0c;既要加首部&#xff0c;也要加尾部&#xff0c;原因是&#xff0c;在网络信息中&#xff0c;帧是以最小单位传输的。所以接收方要正确地接收帧&#xff0c;就必须清楚该帧在一串比特串中…

26_Scala集合常用API汇总

文章目录 1.mkString2.size&#xff0c;length&#xff0c;isEmpty,contains3.reverse ,length,distinct4.获取数据相关4.1数据准备4.2准确获取尾部last4.3 除了最后一个元素不要其他都要4.4从集合获取部分数据 5.删除数据5.1删除3个从左边5.2删除3个右边 6.切分数据splitAt(n:…

前端XHR请求数据

axios封装了XHR(XMLHttpRequest) 效果 项目结构 Jakarta EE9&#xff0c;Web项目。 无额外的maven依赖 1、Web页面 index.html <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title&…

CPU的的处理流程如何快速记忆

为了快速记忆CPU的处理流程&#xff0c;可以将其简化成五个主要阶段&#xff0c;通常称为“冯诺依曼架构”的五个基本步骤&#xff0c;或者是流水线处理的几个阶段。下面是一种便于记忆的简化版本&#xff1a; CPU处理流程的五个阶段&#xff1a; 取指令&#xff08;Instructi…

1.5.2 基于XML配置方式使用Spring MVC

用户登录演示效果 实战概述&#xff0c;可以帮助你更好地理解整个流程。 项目创建 创建了一个名为 SpringMvcDemo01 的 Jakarta EE 项目。通过 Maven 添加了项目所需的依赖&#xff0c;包括 Spring MVC、JSTL 等。 视图层页面 创建了登录页面&#xff08;login.jsp&#xff0…

2.数据类型与变量(java篇)

目录 数据类型与变量 数据类型 变量 整型变量 长整型变量 短整型变量 字节型变量 浮点型变量 双精度浮点型 单精度浮点型 字符型变量 布尔型变量&#xff08;boolean&#xff09; 类型转换 自动类型转换(隐式) 强制类型转换(显式) 类型提升 字符串类型 数据类…

【银角大王——Django课程——靓号搜索实现/单独一篇】

搜索框功能实现 靓号搜索在Django框架中搜索功能实现——底层就是模糊查询添加一个搜索框&#xff0c;使用bootstrap框架将GO&#xff01;修改成一个放大镜靓号列表函数代码解释效果演示 靓号搜索 在Django框架中搜索功能实现——底层就是模糊查询 数字条件用法字符串条件用法…

Gsteamer播放MP4文件

1.概述&#xff1a; gstreamer提供了gst-launch工具&#xff0c;使用该工具我们可以很方便的搭建各种管道&#xff0c;比如gst-launc-1.0 videotestsrc ! autovideosink输入上述命令&#xff0c;我们就能测试视频通路是否OK&#xff0c;但有些场景需要我们提供代码形式。本篇文…

博弈智能的特点

博弈智能是指通过算法和模型对博弈过程进行分析和决策的智能系统。在博弈中&#xff0c;各方参与者追求自身利益和目标&#xff0c;会采取各种策略来达到自己的目标。其中&#xff0c;包括了一些不正当手段&#xff0c;如诡计和欺骗&#xff08;诡&#xff09;&#xff08;诈&a…