java 常量池详解

目录

  • java 常量池详解
    • 一 静态常量池(Static Constant Pool)
      • 1.1 概述
      • 1.2 存储内容
      • 1.3 特点
      • 1.4 示例
    • 二 运行时常量池(Runtime Constant Pool)
      • 2.1 概述
      • 2.2 存储内容
      • 2.3 特点
      • 2.4 示例
    • 三 基础类型常量池(Primitive Type Constant Pool)
      • 3.1 概述
      • 3.2 存储内容
      • 3.3 特点
      • 3.4 示例
      • 3.5 JVM 参数调整缓存范围
    • 四 字符串常量池(String Constant Pool)
      • 4.1 概述
      • 4.2 特点
      • 4.3 存储内容
      • 4.4 工作机制
        • 1. 字符串字面量
        • 2. `String.intern()` 方法
      • 4.5 优点
      • 4.6 注意事项
    • 五 常量池的关系与区别
    • 六 总结

java 常量池详解

在 Java JVM(以 JDK 11 为例)中,常量池是优化内存使用和提高性能的重要机制。常量池主要包括静态常量池运行时常量池基础类型常量池字符串常量池。以下将对这四种常量池进行详细整理和介绍。


一 静态常量池(Static Constant Pool)

1.1 概述

静态常量池是指每个 .class 文件中包含的常量池部分,由 Java 编译器在编译时生成。它主要用于存储在编译期间已知的各种字面量和符号引用。

1.2 存储内容

  1. 字面量(Literals):
    • 数值字面量:如 intlongfloatdoublecharboolean 的常量值。
    • 字符串字面量:如 "Hello World"
  2. 符号引用(Symbolic References):
    • 类或接口的全限定名:如 java/lang/String
    • 字段的名称和描述符:如 ageI(表示 int 类型)。
    • 方法的名称和描述符:如 main([Ljava/lang/String;)V

1.3 特点

  • 编译期生成:静态常量池在编译时就已经确定,并存储在 .class 文件中。
  • 不可变性:一旦编译完成,静态常量池中的内容不可更改。
  • 类级别:每个类文件对应一个静态常量池,多个类文件拥有各自独立的静态常量池。

1.4 示例

假设有如下 Java 类:

public class Example {private static final int CONSTANT_INT = 100;private static final String CONSTANT_STRING = "Hello";public static void main(String[] args) {System.out.println(CONSTANT_INT);System.out.println(CONSTANT_STRING);}
}

在编译后的 Example.class 文件中,静态常量池会包含:

  • 整数 100
  • 字符串 "Hello"
  • java/lang/System
  • 字段 outprintln 方法的符号引用

二 运行时常量池(Runtime Constant Pool)

2.1 概述

运行时常量池是 JVM 在类加载后,静态常量池的一个运行时表示。它是方法区的一部分(在 JDK 8 及之后版本中,属于元空间 Metaspace),用于在程序运行时管理和使用常量。

2.2 存储内容

  1. 来自静态常量池的内容:
    • 字面量(如数值、字符串)。
    • 符号引用解析后的直接引用(如指向内存地址的指针)。
  2. 动态生成的内容:
    • 通过 String.intern() 方法动态添加的字符串。
    • 动态语言的元数据(如在 JVM 上运行的动态语言)。

2.3 特点

  • 动态性:可以在运行时动态添加新的常量,如通过 intern() 方法。
  • 内存位置:在 JDK 8 及之后版本中,运行时常量池位于堆内存的元空间中。
  • 生命周期:与类的生命周期相关,类被卸载时,常量池也会被回收。

2.4 示例

public class RuntimePoolTest {public static void main(String[] args) {String str1 = "Hello"; // 静态常量String str2 = new String("Hello").intern(); // 动态添加到运行时常量池}
}

在上述代码中:

  • "Hello" 是编译时的字符串字面量,已存在于静态常量池。
  • str2 通过 intern() 方法将 "Hello" 的引用添加到运行时常量池。

三 基础类型常量池(Primitive Type Constant Pool)

3.1 概述

基础类型常量池主要针对 Java 中的基本数据类型(如 intlong 等)以及其对应的包装类(如 IntegerLong 等),用于优化内存使用和提高性能。

3.2 存储内容

  1. 基本数据类型的常量值:
    • 如整数 1、浮点数 3.14、字符 'A'、布尔值 true 等。
  2. 包装类的缓存对象:
    • 包装类如 IntegerLongShortByteCharacterBoolean 提供了缓存机制,存储常用的常量值。

3.3 特点

  • 缓存机制:包装类会缓存一定范围内的常量对象,以减少内存开销和提升性能。

  • 范围限制

    • IntegerLong:默认缓存范围是 -128127
    • ShortByte:所有值都在缓存范围内。
    • Character:缓存范围是 \u0000\u007F(即 0127)。
    • Boolean:只有 truefalse 两个值被缓存。
  • 可调整性:某些包装类的缓存范围可以通过 JVM 参数进行调整(如 Integer 的缓存上限)。

3.4 示例

public class PrimitiveConstantPoolTest {public static void main(String[] args) {Integer a = 127;Integer b = 127;Integer c = 128;Integer d = 128;System.out.println(a == b); // true,引用同一个缓存对象System.out.println(c == d); // false,超出缓存范围,创建新对象}
}

在上述代码中:

  • ab 都被自动装箱为 Integer 类型,且值在缓存范围内,因此引用同一个对象。
  • cd 的值超出默认缓存范围,因此分别创建了不同的对象。

3.5 JVM 参数调整缓存范围

对于 Integer,可以通过以下 JVM 参数调整缓存上限:

-Djava.lang.Integer.IntegerCache.high=1000

这会将 Integer 的缓存范围扩展到 -1281000


四 字符串常量池(String Constant Pool)

4.1 概述

字符串常量池是 JVM 中专门用于存储字符串字面量和通过 intern() 方法添加的字符串的区域。其主要目的是为了优化字符串的存储,避免重复创建相同内容的字符串对象,从而节省内存并提高性能。

4.2 特点

  1. 位置
    • JDK 7 及之后版本:字符串常量池位于堆内存中,作为普通对象的一部分。
    • JDK 6 及之前版本:字符串常量池位于方法区(永久代 PermGen)。
  2. 唯一性
    • 常量池中的每个字符串都是唯一的。如果两个字符串内容相同,常量池中只存储一个实例。
  3. 动态性
    • 可以在运行时通过 String.intern() 方法动态添加新的字符串到常量池中。

4.3 存储内容

  • 字符串字面量:如 "Hello""World" 等。
  • 通过 intern() 方法添加的字符串

4.4 工作机制

1. 字符串字面量

当创建字符串字面量时,JVM 会:

  • 检查常量池:首先检查字符串常量池中是否已存在相同内容的字符串。
  • 引用或添加
    • 如果存在,直接引用常量池中的字符串。
    • 如果不存在,将该字符串添加到常量池中。

示例代码

public class StringPoolTest {public static void main(String[] args) {String str1 = "Hello";String str2 = "Hello";System.out.println(str1 == str2); // 输出 true,引用同一个对象}
}
2. String.intern() 方法
  • 功能:将字符串对象的引用添加到常量池中,并返回常量池中的引用。
  • 行为:
    • 如果常量池中已存在相同内容的字符串,则返回该引用。
    • 如果不存在,则将当前字符串添加到常量池中,并返回其引用。

示例代码

public class StringInternTest {public static void main(String[] args) {String str1 = new String("Hello");String str2 = str1.intern();String str3 = "Hello";System.out.println(str1 == str2); // 输出 false,str1 在堆中,str2 引用常量池System.out.println(str2 == str3); // 输出 true,str2 和 str3 引用同一常量池中的字符串}
}

4.5 优点

  1. 节省内存:相同内容的字符串只存储一个实例,减少内存开销。
  2. 提高性能:字符串比较操作更高效,因为可以直接比较引用。

4.6 注意事项

  1. 避免滥用 intern()
    • 频繁调用 intern() 可能导致常量池中过多的字符串,增加内存压力和 GC 负担。
  2. 字符串拼接的优化
    • 编译时拼接:如 "Hello" + "World" 会在编译时优化为 "HelloWorld",直接引用常量池中的字符串。
    • 运行时拼接:如使用 StringBuilder 进行动态拼接,结果字符串默认不在常量池中,需显式调用 intern() 才会被添加。

示例代码

public class StringConcatTest {public static void main(String[] args) {String str1 = "Hello" + "World"; // 编译时优化为 "HelloWorld"String str2 = "HelloWorld";System.out.println(str1 == str2); // 输出 trueString str3 = "Hello";String str4 = str3 + "World"; // 运行时拼接String str5 = "HelloWorld";System.out.println(str4 == str5); // 输出 false}
}

五 常量池的关系与区别

下表总结了静态常量池运行时常量池基础类型常量池字符串常量池的主要区别与联系:

特性静态常量池运行时常量池基础类型常量池字符串常量池
定义位置.class 文件中JVM 的方法区(JDK 8 及后为元空间)运行时常量池的一部分堆内存中的专用区域
生成时间编译时类加载时和运行时编译时和运行时编译时和运行时
存储内容字面量、符号引用静态常量池的内容及运行时生成的新常量基本数据类型的常量值及包装类的缓存对象字符串字面量及通过 intern() 添加的字符串
是否可变不可变可动态扩展基本类型常量不可变,包装类缓存可复用对象可通过 intern() 动态添加
内存管理与类文件生命周期一致与类的生命周期一致,受垃圾回收影响缓存对象存储在堆中,受垃圾回收管理存储在堆中,受垃圾回收管理
主要优化目的减少重复字面量和符号引用的存储管理类加载时的常量,支持动态常量的添加减少包装类对象的创建,提升性能减少重复字符串对象的创建,节省内存

六 总结

在 Java JVM 中,常量池机制通过不同类型的常量池(静态常量池、运行时常量池、基础类型常量池和字符串常量池)优化了内存使用和性能:

  • 静态常量池:在编译时确定,存储类级别的常量和符号引用,存在于每个 .class 文件中。
  • 运行时常量池:在类加载后,静态常量池的运行时表示,可以动态添加常量,位于方法区或元空间中。
  • 基础类型常量池:针对基本数据类型和其包装类,提供缓存机制,减少对象创建,提高性能。
  • 字符串常量池:专门用于存储字符串字面量和通过 intern() 添加的字符串,确保字符串唯一性,节省内存。

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

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

相关文章

电脑主机后置音频插孔无声?还得Realtek高清晰音频管理器调教

0 缘起 一台联想电脑,使用Windows 10 专业版32位,电脑主机后置音频插孔一直没有声音,所以音箱是接在机箱前面版的前置音频插孔上的。 一天不小心捱到了音箱的音频线,音频线头断在音频插孔里面了,前置音频插孔因此用不…

【微服务】1、引入;注册中心;OpenFeign

微服务技术学习引入 - 微服务自2016年起搜索指数持续增长,已成为企业开发大型项目的必备技术,中高级java工程师招聘多要求熟悉微服务相关技术。微服务架构介绍 概念:微服务是一种软件架构风格,以专注于单一职责的多个响应项目为基…

UDP_TCP

目录 1. 回顾端口号2. UDP协议2.1 理解报头2.2 UDP的特点2.3 UDP的缓冲区及注意事项 3. TCP协议3.1 报头3.2 流量控制2.3 数据发送模式3.4 捎带应答3.5 URG && 紧急指针3.6 PSH3.7 RES 1. 回顾端口号 在 TCP/IP 协议中,用 “源IP”, “源端口号”…

《Spring Framework实战》2:Spring快速入门

欢迎观看《Spring Framework实战》视频教程 Spring快速入门 目录 1. Java™开发套件(JDK) 2. 集成开发人员环境(IDE) 3. 安装Maven 4. Spring快速入门 4.1. 开始一个新的Spring Boot项目 4.2. 添加您的代码 4.3. 尝…

Kafka集群部署与安装

一、什么是Kafka Kafka是一个Pub-Sub的消息系统,无论是发布还是订阅,都须指定Topic。 Kafka是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者大规模的网站中的所有动作流数据 二、JMS:Java Message Service Java消息服…

利用Deeplearning4j进行 图像识别

目录 图像识别简介 神经网络 感知器 前馈神经网络 自动编码器 受限玻尔兹曼机 深度卷积网络 理解图像内容以及图像含义方面,计算机遇到了很大困难。本章先介绍计算机理解图像教育方面 遇到的难题,接着重点讲解一个基于深度学习的解决方法。我们会…

PTA DS 基础实验3-2.1 一元多项式求导

基础实验3-2.1 一元多项式求导 分数 20 全屏浏览 切换布局 作者 DS课程组 单位 浙江大学 设计函数求一元多项式的导数。 输入格式: 以指数递降方式输入多项式非零项系数和指数(绝对值均为不超过1000的整数)。数字间以空格分隔。 注意&#xff1a…

redis源码系列--(四)--redis cluster

本文主要记录redis cluster相关流程 2024/11/11 10:19 redis cluster命令文档:https://redis.io/docs/latest/commands/cluster-failover/ redis集群模式和哨兵模式是不同的模式,别搞混了 --chatgpt https://blog.csdn.net/zzti_erlie/article/detail…

vue elementUI Plus实现拖拽流程图,不引入插件,纯手写实现。

vue elementUI Plus实现拖拽流程图,不引入插件,纯手写实现。 1.设计思路:2.设计细节3.详细代码实现 1.设计思路: 左侧button列表是要拖拽的组件。中间是拖拽后的流程图。右侧是拖拽后的数据列表。 我们拖动左侧组件放入中间的流…

java基础学习(接口和抽象类的区别)

1.接口和抽象类的区别 它们都无法直接实例化 接口:使用 interface 关键字定义,接口中的所有方法默认是 public abstract,即使不写修饰符。接口不包含任何实现代码。抽象类:使用 abstract 关键字定义,可以包含抽象方法…

DC系列之DC-8渗透测试

DC-8 靶机渗透测试实战 靶机下载地址: https://download.vulnhub.com/dc/DC-8.zip(下载速度慢可以用迅雷下载) 一、实验环境 实验环境: kali2024:192.168.234.145(nat模式) 靶机环境DC-7&#…

自定义有序Map

package cn.ziqirj.common.utils;import lombok.Getter; import lombok.Setter;import java.util.ArrayList; import java.util.List;/*** 模拟Map集合&#xff0c;key不可重复&#xff0c;按插入顺序排序* author zhangji** param <T>*/ public class CustomOrderlyMap&…

matlab中高精度计算函数vpa与非厄米矩阵本征值的求解

clear;clc;close all tic %并行设置% delete(gcp(nocreate));%关闭之前的并行 cparcluster(local); c.NumWorkers50;%手动设置线程数(否则默认最大线程为12) parpool(c, c.NumWorkers); %并行设置%w1; u2.5;N30;valstozeros(2*N2,100); v10linspace(-3,3,100).;parfor jj1:leng…

机器人领域的一些仿真器

模拟工具和环境对于开发、测试和验证可变形物体操作策略至关重要。这些工具提供了一个受控的虚拟环境&#xff0c;用于评估各种算法和模型的性能&#xff0c;并生成用于训练和测试数据驱动模型的合成数据。 Bullet Physics Library 用于可变形物体模拟的一个流行的物理引擎是 B…

【玩转23种Java设计模式】行为型模式篇:备忘录模式

软件设计模式&#xff08;Design pattern&#xff09;&#xff0c;又称设计模式&#xff0c;是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。 汇总目录链接&…

GraphRAG vs 传统 RAG:如何通过知识图谱提升 AI 检索能力

相比传统 RAG 仅能独立检索文本片段的局限性&#xff0c;GraphRAG通过构建实体关系图谱实现了信息间的连接&#xff0c;让 AI 能更完整地理解和检索复杂的关联信息&#xff0c;从而生成更准确和连贯的回答 问题背景: 想象有一本详细记录某人(X)成就的传记,每个章节都描述了他的…

【小制作】米家模拟手指点击

代码功能解释 这段代码是一个基于Arduino平台的控制程序&#xff0c;主要功能包括&#xff1a; 初始化&#xff1a;设置引脚模式、初始化编码器、舵机和EEPROM。按键检测&#xff1a;处理按钮的单击、双击和长按事件&#xff0c;并根据事件执行相应操作。编码器更新&#xff…

23. 【.NET 8 实战--孢子记账--从单体到微服务】--记账模块--预算

在每个月发工资后很多人会对未来一个月的花销进行大致的计划&#xff0c;这个行为叫做预算。那么在这篇文章中我们将一起开发预算服务。 一、需求 预算需求就是简单的增删改查&#xff0c;虽然比较简单&#xff0c;但是也有几点需要注意。 编号需求说明1新增预算1. 针对每种…

Pentaho Kettle迁移至Oracle的空字符串和NULL的问题处理,大坑!

一、问题说明 在使用 Kettle 将 DB2 数据迁移到 Oracle 的过程中&#xff0c;出现了 DB2 中为空字符串的字段&#xff0c;在插入到 Oracle 过程中实际插入的为 NULL &#xff0c;导致触发了非空校验而迁移失败 空字符串 ‘’ &#xff0c;即长度为0的字符串 搜索该问题后得知…

2025-01-04 Unity插件 YodaSheet1 —— 插件介绍

文章目录 1 介绍2 工作原理2.1 ScriptableObject -> YadeSheetData2.2 YadeDatabase 存储多个 YadeSheetData 3 用途4 缺点5 推荐 1 介绍 ​ Yade 提供类似于 Excel 或者 Google Sheets 的表格编辑器&#xff0c;可以轻松地在 Unity 编辑器中 编辑&#xff0c;搜索&#xf…