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 中的字符串常量池和运行时常量池的更多相关文章

  1. Java中的常量池(字符串常量池、class常量池和运行时常量池)

    转载. https://blog.csdn.net/zm13007310400/article/details/77534349 简介: 这几天在看Java虚拟机方面的知识时,看到了有几种不同常量池的 ...

  2. java常量池-字符串常量池、class常量池和运行时常量池

    原文链接:http://tangxman.github.io/2015/07/27/the-difference-of-java-string-pool/ 在java的内存分配中,经常听到很多关于常量 ...

  3. in C#,编译型常量(const)和运行时常量(readonly)

    readonly 关键字与 const 关键字不同. const 字段只能在该字段的声明中初始化. readonly 字段可以在声明或构造函数中初始化. 因此,根据所使用的构造函数, readonly ...

  4. c# 编译期常量const和运行时常量readonly

    注意:const编译期常量是编译的时候就确定的,可以查看IL代码,是写死的.如果另一个程序集引用后,该程序集没有进行编译,则值不会改变. 看效果: 项目中有2个程序集: 其中:常量在这个程序集中定义并 ...

  5. Java方法区和运行时常量池溢出问题分析

    运行时常量池是方法区的一部分,方法区用于存放Class的相关信息,如类名.访问修饰符.常量池.字段描述.方法描述等. String.intern()是一个native方法,它的作用是:如果字符串常量池 ...

  6. Java方法区和运行时常量池溢出问题分析(转)

    运行时常量池是方法区的一部分,方法区用于存放Class的相关信息,如类名.访问修饰符.常量池.字段描述.方法描述等. String.intern()是一个native方法,它的作用是:如果字符串常量池 ...

  7. 转载:Java中的字符串常量池详细介绍

    引用自:http://blog.csdn.net/langhong8/article/details/50938041 这篇文章主要介绍了Java中的字符串常量池详细介绍,JVM为了减少字符串对象的重 ...

  8. Java中的字符串常量池,栈和堆的概念

    问题:String str = new String(“abc”),“abc”在内存中是怎么分配的?    答案是:堆内存.(Tips:jdk1.8 已经将字符串常量池放在堆内存区) 题目考查的为Ja ...

  9. 正确理解和使用JAVA中的字符串常量池

    前言 研究表明,Java堆中对象占据最大比重的就是字符串对象,所以弄清楚字符串知识很重要,本文主要重点聊聊字符串常量池.Java中的字符串常量池是Java堆中的一块特殊存储区域,用于存储字符串.它的实 ...

  10. Java编译时常量和运行时常量

    Java编译时常量和运行时常量 编译期常量指的就是程序在编译时就能确定这个常量的具体值. 非编译期常量就是程序在运行时才能确定常量的值,因此也称为运行时常量. 在Java中,编译期常量指的是用fina ...

随机推荐

  1. Q: 导出的照片时,批量修改创建时间和修改时间。

    使用PowerShell脚本批量修改目录下所有照片(包括但不限于.jpg, .jpeg, .png等格式)的创建时间和最后修改时间,你可以采用以下脚本.这个脚本将遍历指定目录及其子目录下的所有支持的照 ...

  2. [大模型/AI/GPT] Chatbox:大模型可视化终端应用

    序 概述:Chatbox AI Chatbox AI 是一款 AI 客户端应用和智能助手,支持众多先进的 AI 模型和 API,可在 Windows.MacOS.Android.iOS.Linux 和 ...

  3. GUI编程之AWT

    介绍 包含了很多类和接口 元素:窗口.按钮.文本框 java.awt Frame 就是一个窗口 实现 package com.yeyue.lesson01;​import java.awt.*;​pu ...

  4. 关于DateTime的自定义转换

    关于DateTime的自定义转换.把字符串时间转换成可以供DateTime类型识别的字符串类型的粗略实现. /// <summary> /// 把从数据库中读取的字符串的CurrentTi ...

  5. 你还不会使用Pycham Remote development 打开远程主机工作目录吗?这篇文章帮你解决!

    前言 必备: 本地开发机与远程主机都要安装Pycharm专业版!!! 废话不多说直接开始!! 1.打开pycharm 2.依次点击File.Remote Development 3.依次点击SSH.N ...

  6. Ansible - [01] 入门&安装部署

    自动化运维工具,可以批量远程其他主机并进行管理操作 一.什么是 Ansible Ansible首次发布于2012年,作者:Michael DeHaan,同时也是Cobbler的作者,Ansible于2 ...

  7. 青岛oj集训1

    2025/3/4 内容:有向无环图(DAG) 优点:DAG有很多良好性质 拓扑排序 用处:可以根据拓扑序进行dp 这次计算所用的所有边的权值都是有计算过的 一张DAG图肯定有拓扑序(bfs序,dfs序 ...

  8. Navicat 如何将表恢复默认状态下

    场景: 测试一套流程后,造测试数据非常麻烦的情况下,如何通过更改数据库为默认情况即初始表数据 案例: 比如表原有结构如下图(一) 修改后数据如下图(二): 需求:将图二数据恢复到图一内容下 操作思想: ...

  9. httprunner 断言报错 expect_value 和check_value类型不一致

    csv 源码 drillCode,drillName,code,msg y500,牙轮500,200,操作成功 YL201,牙轮201,500,牙轮编号已存在 运行后报错 E httprunner.e ...

  10. Qt源码阅读(二) moveToThread

    Qt 源码分析之moveToThread 这一次,我们来看Qt中关于将一个QObject对象移动至一个线程的函数moveToThread 目录 Qt 源码分析之moveToThread Qt使用线程的 ...