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 ...
随机推荐
- Q: 导出的照片时,批量修改创建时间和修改时间。
使用PowerShell脚本批量修改目录下所有照片(包括但不限于.jpg, .jpeg, .png等格式)的创建时间和最后修改时间,你可以采用以下脚本.这个脚本将遍历指定目录及其子目录下的所有支持的照 ...
- [大模型/AI/GPT] Chatbox:大模型可视化终端应用
序 概述:Chatbox AI Chatbox AI 是一款 AI 客户端应用和智能助手,支持众多先进的 AI 模型和 API,可在 Windows.MacOS.Android.iOS.Linux 和 ...
- GUI编程之AWT
介绍 包含了很多类和接口 元素:窗口.按钮.文本框 java.awt Frame 就是一个窗口 实现 package com.yeyue.lesson01;import java.awt.*;pu ...
- 关于DateTime的自定义转换
关于DateTime的自定义转换.把字符串时间转换成可以供DateTime类型识别的字符串类型的粗略实现. /// <summary> /// 把从数据库中读取的字符串的CurrentTi ...
- 你还不会使用Pycham Remote development 打开远程主机工作目录吗?这篇文章帮你解决!
前言 必备: 本地开发机与远程主机都要安装Pycharm专业版!!! 废话不多说直接开始!! 1.打开pycharm 2.依次点击File.Remote Development 3.依次点击SSH.N ...
- Ansible - [01] 入门&安装部署
自动化运维工具,可以批量远程其他主机并进行管理操作 一.什么是 Ansible Ansible首次发布于2012年,作者:Michael DeHaan,同时也是Cobbler的作者,Ansible于2 ...
- 青岛oj集训1
2025/3/4 内容:有向无环图(DAG) 优点:DAG有很多良好性质 拓扑排序 用处:可以根据拓扑序进行dp 这次计算所用的所有边的权值都是有计算过的 一张DAG图肯定有拓扑序(bfs序,dfs序 ...
- Navicat 如何将表恢复默认状态下
场景: 测试一套流程后,造测试数据非常麻烦的情况下,如何通过更改数据库为默认情况即初始表数据 案例: 比如表原有结构如下图(一) 修改后数据如下图(二): 需求:将图二数据恢复到图一内容下 操作思想: ...
- httprunner 断言报错 expect_value 和check_value类型不一致
csv 源码 drillCode,drillName,code,msg y500,牙轮500,200,操作成功 YL201,牙轮201,500,牙轮编号已存在 运行后报错 E httprunner.e ...
- Qt源码阅读(二) moveToThread
Qt 源码分析之moveToThread 这一次,我们来看Qt中关于将一个QObject对象移动至一个线程的函数moveToThread 目录 Qt 源码分析之moveToThread Qt使用线程的 ...