Java 中的字符串常量池和运行时常量池
Java 中的字符串常量池和运行时常量池
1. 字符串常量池(String Constant Pool)
定义
字符串常量池是 JVM 内存中专门用于存储字符串字面量和通过 intern() 方法加入的字符串对象的区域。它的目的是避免重复创建相同的字符串对象,以节省内存,并提高性能。
如何工作
- 每当遇到一个字符串字面量(如
"Hello"),JVM 会首先检查这个字符串是否已经存在于字符串常量池中。如果已存在,它就返回该字符串的引用;如果不存在,它就将这个字符串加入常量池,并返回该字符串的引用。 - 由于字符串是不可变的,JVM 通过这种方式共享字符串对象,避免了大量相同内容字符串的冗余存储。
示例
public class StringConstantPool {
public static void main(String[] args) {
String str1 = "Hello"; // 字符串字面量
String str2 = "Hello"; // 字符串字面量
System.out.println(str1 == str2); // 输出 true
}
}
解释
- str1 和 str2 都是 "Hello" 字符串字面量,在字符串常量池中只有一个 "Hello" 字符串对象,因此它们指向同一个内存地址。str1 == str2 返回 true,说明它们是同一个对象。
使用 intern() 方法
- 如果字符串是通过 new 关键字创建的,它并不会直接进入字符串常量池,而是新创建一个对象。但你可以通过 intern() 方法显式将其添加到常量池中。
示例
public class StringInternExample {
public static void main(String[] args) {
String str1 = new String("Hello");
String str2 = str1.intern(); // 将字符串 "Hello" 添加到常量池
String str3 = "Hello"; // 直接从常量池中获取 "Hello"
System.out.println(str1 == str2); // 输出 false,str1 是通过 new 创建的
System.out.println(str2 == str3); // 输出 true,str2 和 str3 都指向常量池中的 "Hello"
}
}
解释
- str1 是通过 new 创建的,因此它指向堆内存中的对象,而不是字符串常量池中的对象。
- str2 调用了 intern(),它将 "Hello" 加入常量池,并返回池中的引用,因此 str2 和 str3 指向相同的对象。
1. 运行时常量池(Runtime Constant Pool)
定义:
每个 .class 文件中都包含了一个常量池,它存储了类中的各种符号引用,包括类名、字段名、方法名、接口名等。类加载时,JVM 会解析这些符号引用,将其转化为实际的内存地址(直接引用)。
如何工作:
- 常量池包含:常量池不仅包含字符串常量,还包括类、字段、方法等符号引用。
- 在类加载时,常量池中的符号引用会被替换为直接引用,这些直接引用指向内存中的具体对象或方法等。
示例:
public class RuntimeConstantPool {
public static final String CONSTANT = "Hello, World"; // 存储在运行时常量池
public static void main(String[] args) {
System.out.println(CONSTANT); // 直接使用运行时常量池中的常量
}
}
解释
- CONSTANT 被标记为 final,它是一个编译时常量,并会被存储在类的运行时常量池中。
- 当程序执行时,常量 CONSTANT 会被直接引用,并打印出 "Hello, World"。
常量池中符号引用的解析
- 符号引用:指的是在类文件中记录的引用,如方法名、字段名等。
- 解析过程:当类加载时,JVM 会将这些符号引用解析为实际的内存地址,称为直接引用。
示例
public class SymbolicReferenceExample {
public static void main(String[] args) {
Class<?> clazz = String.class; // 符号引用:类名
System.out.println(clazz.getName()); // 解析:打印类名
}
}
解释
- clazz 是一个符号引用,表示 String.class 类,JVM 在类加载过程中会解析这个符号引用,并将其转化为直接引用,从而获得类的内存地址。
3. 字符串常量池与运行时常量池的关系
- 字符串常量池是运行时常量池的一部分,专门存储字符串字面量。在类加载过程中,所有的字符串字面量都会被存储在字符串常量池中,并可以共享使用。
- 运行时常量池包含了更多类型的常量,不仅仅是字符串,还包括其他类、方法、字段等的符号引用。
public class ConstantPoolExample {
public static final String STR_CONSTANT = "Hello";
public static final int NUM_CONSTANT = 100;
public static void main(String[] args) {
System.out.println(STR_CONSTANT); // 来自字符串常量池
System.out.println(NUM_CONSTANT); // 来自运行时常量池
}
}
解释
- STR_CONSTANT 存储在字符串常量池中。
- NUM_CONSTANT 存储在类的运行时常量池中。
- 两者都在类的加载过程中被解析,分别对应各自的常量池区域。
总结
- 字符串常量池:只用于存储字符串字面量和 intern() 添加的字符串对象,避免重复创建相同的字符串对象,提高内存使用效率。
- 运行时常量池:存储类中的各种常量和符号引用,包含了字符串字面量、类名、字段名、方法名等。在类加载时,JVM 将这些符号引用解析为直接引用。
- 优化内存和性能:通过共享常量池中的常量,JVM 避免了大量重复的对象创建,从而节省内存,提升了性能。
Java 中的字符串常量池和运行时常量池的更多相关文章
- Java中的常量池(字符串常量池、class常量池和运行时常量池)
转载. https://blog.csdn.net/zm13007310400/article/details/77534349 简介: 这几天在看Java虚拟机方面的知识时,看到了有几种不同常量池的 ...
- java常量池-字符串常量池、class常量池和运行时常量池
原文链接:http://tangxman.github.io/2015/07/27/the-difference-of-java-string-pool/ 在java的内存分配中,经常听到很多关于常量 ...
- in C#,编译型常量(const)和运行时常量(readonly)
readonly 关键字与 const 关键字不同. const 字段只能在该字段的声明中初始化. readonly 字段可以在声明或构造函数中初始化. 因此,根据所使用的构造函数, readonly ...
- c# 编译期常量const和运行时常量readonly
注意:const编译期常量是编译的时候就确定的,可以查看IL代码,是写死的.如果另一个程序集引用后,该程序集没有进行编译,则值不会改变. 看效果: 项目中有2个程序集: 其中:常量在这个程序集中定义并 ...
- Java方法区和运行时常量池溢出问题分析
运行时常量池是方法区的一部分,方法区用于存放Class的相关信息,如类名.访问修饰符.常量池.字段描述.方法描述等. String.intern()是一个native方法,它的作用是:如果字符串常量池 ...
- Java方法区和运行时常量池溢出问题分析(转)
运行时常量池是方法区的一部分,方法区用于存放Class的相关信息,如类名.访问修饰符.常量池.字段描述.方法描述等. String.intern()是一个native方法,它的作用是:如果字符串常量池 ...
- 转载:Java中的字符串常量池详细介绍
引用自:http://blog.csdn.net/langhong8/article/details/50938041 这篇文章主要介绍了Java中的字符串常量池详细介绍,JVM为了减少字符串对象的重 ...
- Java中的字符串常量池,栈和堆的概念
问题:String str = new String(“abc”),“abc”在内存中是怎么分配的? 答案是:堆内存.(Tips:jdk1.8 已经将字符串常量池放在堆内存区) 题目考查的为Ja ...
- 正确理解和使用JAVA中的字符串常量池
前言 研究表明,Java堆中对象占据最大比重的就是字符串对象,所以弄清楚字符串知识很重要,本文主要重点聊聊字符串常量池.Java中的字符串常量池是Java堆中的一块特殊存储区域,用于存储字符串.它的实 ...
- Java编译时常量和运行时常量
Java编译时常量和运行时常量 编译期常量指的就是程序在编译时就能确定这个常量的具体值. 非编译期常量就是程序在运行时才能确定常量的值,因此也称为运行时常量. 在Java中,编译期常量指的是用fina ...
随机推荐
- LeetCode 力扣 205. 同构字符串
给定两个字符串 s 和 t ,判断它们是否是同构的. 如果 s 中的字符可以按某种映射关系替换得到 t ,那么这两个字符串是同构的. 每个出现的字符都应当映射到另一个字符,同时不改变字符的顺序.不同字 ...
- ReentrantLock实现机制
掌握Reentrantlock 具体结构 下文Reentrantlock简称RL,阅读之前强烈建议读一下AQS源码解析: https://www.cnblogs.com/seamount3/p/186 ...
- 通过串口通信 对TCP传输层以下的理解
这可能是近期暂时最后一篇c嵌入式的文章了 基础的串口使用 参照网上的stm32教程套路引入标准库,初始化芯片手册上对应串口引脚 ,初始化stm32串口功能,然后有数据了就自然在寄存器上,就这样,你的波 ...
- 亮相CCF中国软件大会,天翼云助力千行百业搭上“数字快车”
中国软件领域规模最大.影响最广的学术会议--CCF中国软件大会(ChinaSoft 2023)在上海国际会议中心开幕.CCF理事长.大会执行委员会荣誉zhu席.中国科学院院士梅宏,上海市经济和信息化委 ...
- 硬件设计:POE--POE基础
参考资料:POE供电基础知识:PSE PD检测过程详解 POE供电简介 以太网供电 一.POE相关介绍 POE(Power Over Ethernet)是指在现有的以太网Cat.5布线基础架构上不做任 ...
- Q:Linux查看进程启动服务的精确时间
输入命令 获取进程号 #1#获取进程号ps -ef|grep java|grep 进程名#2#获取启动时间ps -p 进程号 -o lstart 展示启动时间:星期 月 日 小时:分钟:秒 年 一键 ...
- 使用Docker编译安装运行Doris
一.编译源码 (1)拉取编译镜像docker pull apache/incubator-doris:build-env-1.2 (2)Mac电脑上拉取源码git clone https://gith ...
- P4774 [NOI2018] 屠龙勇士 题解
传送门 题解 思路 由题目可知,一条龙被攻击 \(x\) 次并回复若干次后生命值恰好为 \(0\) 则死亡,可以得出如下式子: \[\large ATK_i \cdot x \equiv a_i(\m ...
- 探秘Transformer系列之(2)---总体架构
探秘Transformer系列之(2)---总体架构 0x00 概述 0.1 流程 使用Transformer来进行文本生成其实就是用模型来预测下一个词,完整流程包括多个阶段,如分词.向量化.计算注意 ...
- 你还不会使用Pycham Remote development 打开远程主机工作目录吗?这篇文章帮你解决!
前言 必备: 本地开发机与远程主机都要安装Pycharm专业版!!! 废话不多说直接开始!! 1.打开pycharm 2.依次点击File.Remote Development 3.依次点击SSH.N ...