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. 尝…

利用Deeplearning4j进行 图像识别

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

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

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

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

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

DC系列之DC-8渗透测试

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

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…

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

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

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

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

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

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

VScode 格式化代码空格记录

点击 -> “文件” -> “首选项" -> “设置” -> 按下图操作: 怎么格式化代码空格,先看下: 保存代码后,这代码自动格式化发,如下图: 你可以试试看就即可

苹果系统MacOS下ObjectC建立的App程序访问opencv加载图片程序

前言 苹果系统下使用opencv感觉还是有些不太方便,总是感觉有点受到限制。本博客描述的是在MacOS下建立App程序然后调用opencv显示图片时出现的一些问题并最后解决的一个过程。 一、程序的建立 选择程序的类型: 选择界面模式和编程语言: 其余…

CSS3——3. 书写格式二

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title></title></head><body><!--css书写&#xff1a;--><!--1. 属性名:属性值--><!--2.属性值是对属性的相关描述--><!--3.属性名必须是…

windows配置jdk

进入此网址 JDK 11 Releases 找到11版本进行下载 下载好后配置环境变量 按windows加r键输入sysdm.cpl点击确定 在系统变量里新建输入 JAVA_HOME D:\openjdk-1128_windows-x64_bin\jdk-11 //变量值为安装jidk的路径 配置path环境变量 编辑path变量输入%JAVA_HOME%\bin并放置最…

深入刨析数据结构之排序(下)

目录 1.内部排序 1.5选择排序 1.5.1简单选择排序 1.5.2树形选择排序 1.6堆排序 1.7归并排序 1.7.1递归归并 1.7.2非递归归并 1.8计数排序 1.9基数排序 常见内部排序的总结&#xff1a; 1.内部排序 1.5选择排序 选择排序&#xff08;Selection Sort&#xff09;的基…

RocketMQ场景问题

1.消息丢失 有这么一个场景&#xff0c;就是订单支付完成之后&#xff0c;订单系统会进行发送消息给RocketMQ集群&#xff0c;下游会有积分系统进行监听这个消息&#xff0c;进行消费然后给用户发放积分。在下面的这个场景中&#xff0c;通过查询日志发现了订单系统发送订单支付…

cordova项目环境搭建 hello

环境准备&#xff1a; 1.下载nodejs并安装配置。 直接官网下载最新版本。此次我是下载的 Node.js v22.12.0。 1.1安装好后配置镜像。 输入&#xff1a;node -v // 显示node.js版本 npm -v // 显示npm版本 1.2环境配置 &#xff08;1&#xff09;找到安装的目录&am…

LE Audio 初探

LE Audio 架构 一.LE Audio profile框架 profile初识&#xff1a; BAP&#xff1a; BAP通常在手机端实现&#xff0c;它允许配置编解码器&#xff0c;配置QoS(质量服务)&#xff0c;控制流媒体&#xff0c; PACS&#xff1a; Published Audio Capabilities Service&#xff0c;…