当前位置: 首页 > article >正文

JVM常量池(class文件常量池,运行时常量池,字符串常量池)

文章目录

  • 问题
  • JVM运行时数据区
  • JVM中的常量池
    • Class文件常量池
    • 运行时常量池
    • 字符串常量池
      • 创建了几个对象
      • String的定义
      • intern()
      • 问题

超过1W字深度剖析JVM常量池(全网最详细最有深度) - 跟着Mic学架构 - 博客园

问题

jdk1.8之后

  • 元空间是独立存在的?还是位于方法区?
  • 字符串常量池在堆中还是独立存在?

在 JDK 1.8 及之后版本:

  1. 元空间与方法区关系元空间可以理解为是方法区的一种实现 。JDK 1.8 移除了永久代,引入元空间来替代。元空间使用本地内存(Native Memory ),不再像永久代那样在 JVM 堆内存中划分一块固定区域。此时方法区的功能由元空间来承担,运行时常量池等原本在方法区的部分也存在于元空间中,但元空间和传统意义上方法区概念并非完全等同,只是功能上有继承关系。 所以说元空间是方法区新的实现形式,它不是独立于方法区概念存在,而是改变了方法区的实现方式
  2. 字符串常量池位置字符串常量池在堆中。JDK 1.7 时,字符串常量池从方法区(永久代 )迁移到堆中,JDK 1.8 延续了这一做法。在堆中的字符串常量池,用于存储字符串字面量以及通过String.intern()方法添加进去的字符串 。其物理位置位于堆中,逻辑归属于方法区,从 Java 虚拟机规范角度,它是方法区相关概念的一部分,承担着存储字符串字面量等常量的功能,和方法区其他组成部分(如运行时常量池等)有紧密逻辑关联。

JVM运行时数据区

从类加载,到JVM运行时数据区的整体结构画出来,如下图所示(元空间位于本地内存)。Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域都有各自的用途。

JVM运行时数据区在物理内存上并不是单一的连续内存块,而是由多个不同物理来源的内存区域组合而成的逻辑概念

在这里插入图片描述

JVM中的常量池

JVM中的常量池可以分成以下几类:

  1. Class文件常量池/静态常量池
  2. (全局)字符串常量池
  3. 运行时常量池

Class文件常量池

程序编译后可以通过javap -c 类名.class查看该类的字节码文件。或者使用jclasslib插件。

在这里插入图片描述

  1. 位置:存在于编译后的.class文件中,是.class文件的一部分。

    • 在编译阶段,静态常量池在.class文件中,此时还未加载到JVM内存,纯属磁盘上的文件内容。
    • 当类被JVM加载时,静态常量池的内容会被解析并存入运行时常量池(Runtime Constant Pool)。
  2. 作用:存储编译期生成的各种字面量和符号引用。

    • 字面量:指的是在源代码中直接给出的数据值,在源代码中直接写出的数值,字符串,布尔等。比如文本字符串(String a = “Hello World”);声明为final的常量值(private int value = 1);基本数据类型的值。

    • 符号引用

      • 类和接口的全限定名。也就是Ljava/lang/String;,主要用于在运行时解析得到类的直接引用。

          #23 = Utf8               ([Ljava/lang/String;)V#25 = Utf8               [Ljava/lang/String;#27 = Utf8               Ljava/lang/String;
        
      • 字段的名称和描述符。字段也就是类或者接口中声明的变量,包括类级别变量(static)实例级的变量

      • 方法的名称和描述符。方法的描述类似于JNI动态注册时的“方法签名”,也就是参数类型+返回值类型,比如下面的这种字节码,表示main方法和String返回类型。

          #19 = Utf8               main#20 = Utf8               ([Ljava/lang/String;)V
        

运行时常量池

运行时常量池就是每一个类或接口的常量池(constant pool)在运行时的表现形式。

一个类在加载的过程,会经历加载连接(验证、准备、解析)初始化,而在类加载这个阶段,需要做以下几件事情:

  1. 通过一个类的全类限定名获取此类的二进制字节流。
  2. 在堆内存生成一个java.lang.Class对象,代表加载这个类,做为这个类的入口。
  3. class字节流的静态存储结构转化成方法区(元空间)的运行时数据结构。

而第三点就包含了class文件常量池进入运行时常量池的过程。

所以,运行时常量池的作用是存储class文件常量池中的符号信息,在类的解析阶段会把这些符号引用转换成直接引用(实例对象的内存地址),翻译出来的直接引用也是存储在运行时常量池中。class文件常量池的大部分数据会被加载到运行时常量池。

虽然方法区(或元空间)是全局共享的内存区域,但每个类都有自己独立的运行时常量池

运行时常量池是 动态的符号引用解析中心,其核心行为可归纳为:

  1. 存储:承载 Class 文件常量池的原始符号信息
  2. 转换:在解析阶段将符号引用替换为直接引用
  3. 扩展:支持运行时动态添加新常量
  4. 协同:与字符串常量池、方法区元数据紧密交互

字符串常量池

位置

虽然从物理位置上看,它在堆空间内,但它是一个逻辑上独立的区域。

存在意义

JVM之所以单独设计字符串常量池,是JVM为了提高性能以及减少内存开销的一些优化:

  1. String对象作为Java语言中重要的数据类型,是内存中占据空间最大的一个对象。高效地使用字符串,可以提升系统的整体性能。
  2. 创建字符串常量时,首先检查字符串常量池是否存在该字符串,如果有,则直接返回该引用实例,不存在,则实例化该字符串放入常量池中。

举例

String a="Hello";String b=new String("Mic");
  1. a这个变量,是在编译期间就已经确定的,会进入到字符串常量池。
  2. b这个变量,是通过new关键字实例化,new是创建一个对象实例并初始化该实例,因此这个字符串对象是在运行时才能确定的,创建的实例在堆空间上。

在这里插入图片描述

创建了几个对象

深入理解Java字符串常量池 | 二哥的Java进阶之路

String s = new String("二哥");

使用 new 关键字创建一个字符串对象时,Java 虚拟机会先在字符串常量池中查找有没有‘二哥’这个字符串对象,如果有,就不会在字符串常量池中创建‘二哥’这个对象了,直接在堆中创建一个‘二哥’的字符串对象,然后将堆中这个‘二哥’的对象地址返回赋值给变量 s。

如果没有,先在字符串常量池中创建一个‘二哥’的字符串对象,然后再在堆中创建一个‘二哥’的字符串对象,然后将堆中这个‘二哥’的字符串对象地址返回赋值给变量 s。

对于这行代码 String s = new String("二哥");,它创建了两个对象:一个是字符串对象 “二哥”,它被添加到了字符串常量池中,另一个是通过 new String() 构造方法创建的字符串对象 “二哥”,它被分配在堆内存中,同时引用变量 s 存储在栈上,它指向堆内存中的字符串对象 “二哥”。

在这里插入图片描述

String的定义

public final class Stringimplements java.io.Serializable, Comparable<String>, CharSequence {/** The value is used for character storage. */private final char value[];/** Cache the hash code for the string */private int hash; // Default to 0}

从上述源码中可以发现。

  1. String这个类是被final修饰的,代表该类无法被继承。
  2. String这个类的成员属性value[]也是被final修饰,代表该成员属性不可被修改。

因此String具有不可变的特性,也就是说String一旦被创建,就无法更改。这么设计的好处有几个。

  1. 方便实现字符串常量池: 在Java中,由于会大量的使用String常量,如果每一次声明一个String都创建一个String对象,那将会造成极大的空间资源的浪费。Java提出了String pool的概念,在堆中开辟一块存储空间String pool,当初始化一个String变量时,如果该字符串已经存在了,就不会去创建一个新的字符串变量,而是会返回已经存在了的字符串的引用。如果字符串是可变的,某一个字符串变量改变了其值,那么其指向的变量的值也会改变,String pool将不能够实现!
  2. 线程安全性,在并发场景下,多个线程同时读一个资源,是安全的,不会引发竞争,但对资源进行写操作时是不安全的,不可变对象不能被写,所以保证了多线程的安全。
  3. 保证 hash 属性值不会频繁变更。确保了唯一性,使得类似HashMap容器才能实现相应的key-value缓存功能,于是在创建对象时其hashcode就可以放心的缓存了,不需要重新计算。这也就是Map喜欢将String作为Key的原因,处理速度要快过其它的键对象。所以HashMap中的键往往都使用String。

intern()

  1. JDK文档中关于intern()方法的说明:当调用intern方法时,如果常量池(内置在 JVM 中的)中已经包含相同的字符串,则返回池中的字符串。否则,将此String对象添加到池中,并返回对该String对象的引用。‘
  2. 注意,所有字符串字面量在初始化时,会默认调用intern()方法。
  3. intern() 方法的核心逻辑
    • 检查常量池:JVM 会通过内容匹配(而非引用相等)查找常量池中是否已有该字符串。
    • 处理结果
      • 若存在:返回常量池中的引用(可能指向堆中的对象)。
      • 若不存在:JDK 7 及以后直接在常量池中记录堆中对象的引用,而非复制内容(节省内存),并返回该引用。

问题

public static void main(String[] args) {String a = new String(new char[]{'a', 'b', 'c'});  // 堆中创建新对象,常量池未变化String b = a.intern();                              // 将a的内容放入常量池(如果不存在),并返回池中的引用System.out.println(a == b);                         // true:此时常量池中的引用就是a本身
}

1. 为什么 new String(char[]) 不会自动调用 intern()

当使用 new String(char[]) 构造函数时,JVM 会创建一个全新的String对象,但不会自动将其放入常量池。这是因为:

  • 常量池的设计初衷:常量池主要用于存储编译期已知的字符串字面量(如 "abc")或显式调用 intern() 的字符串。
  • 性能考量:如果每次通过字符数组创建字符串都自动入池,会导致常量池膨胀,影响性能和内存占用。
  • 语义一致性new 关键字的语义是 “强制创建新对象”,即使常量池中已有相同内容的字符串,new String(char[]) 也会创建独立的对象。

3. 总结:何时字符串会进入常量池?

  • 编译期已知的字面量(如 "abc")会在类加载时自动进入常量池。
  • 运行时动态创建的字符串(如通过字符数组、substring() 等方法)不会自动入池,除非显式调用 intern()

相关文章:

JVM常量池(class文件常量池,运行时常量池,字符串常量池)

文章目录 问题JVM运行时数据区JVM中的常量池Class文件常量池运行时常量池字符串常量池创建了几个对象String的定义intern()问题 超过1W字深度剖析JVM常量池&#xff08;全网最详细最有深度&#xff09; - 跟着Mic学架构 - 博客园 问题 jdk1.8之后 元空间是独立存在的&#xf…...

开源免费无广告专注PDF编辑、修复和管理工具 办公学术 救星工具

各位PDF处理小能手们&#xff01;我跟你们说啊&#xff0c;今天要给大家介绍一款超牛的国产开源PDF处理工具&#xff0c;叫PDFPatcher&#xff0c;也叫PDF补丁丁。它就像一个PDF文档的超级修理工&#xff0c;专门解决PDF编辑、修复和管理的各种难题。 这软件的核心功能和特点&a…...

鸿蒙Flutter实战:24-混合开发详解-4-初始化Flutter

概述 将 Flutter 模块添加至宿主鸿蒙项目中后&#xff0c;接下需要实现页面跳转、消息通信等功能&#xff0c;本文重点介绍如何初始化 Flutter。 项目配置 添加依赖 编辑 ohos_app/oh-package.json 文件 如果通过 Har 包方式引入 Flutter 模块&#xff0c;则需要添加如下内…...

我爱学算法之—— 二分查找(中)

一、搜索插入位置 题目解析 这道题&#xff0c;给定一个数组nums和一个目标值target&#xff0c;让我们在数组nums中找到目标值&#xff1b;如果目标值存在就返回它的下标&#xff0c;如果不存在就返回数target被顺序插入的位置下标。 算法思路 这道题&#xff0c;我们可以使…...

Golang 并发小结

并发问题概览 问题类型描述数据竞争多个协程对共享变量进行非同步读写操作死锁多个协程互相等待对方释放资源活锁协程不断尝试获取资源但始终失败协程泄漏协程未能及时退出&#xff0c;程序中 goroutine 数量飙升Channel 误用通道未关闭、重复关闭、关闭后写入等问题调度抖动非…...

RTC技术

什么是RTC RTC&#xff08;Real time communication&#xff09;实时通信&#xff0c;是实时音视频的一个简称&#xff0c;我们常说的RTC技术一般指的是WebRTC技术&#xff0c;已经被 W3C 和 IETF 发布为正式标准。由于几乎所有主流浏览器都支持 WebRTC 标准 API &#xff0c;…...

基于Matlab建立不同信道模型

在MATLAB中建立不同的信道模型是无线通信系统仿真的重要组成部分。信道模型用于模拟信号在传输过程中受到的各种影响&#xff0c;如衰减、多径效应、噪声等。以下是一些常见的信道模型及其在MATLAB中的实现方法&#xff1a; 1. 理想信道模型 理想信道假设信号在传输过程中不受…...

uni-app 排坑

记录代码中遇到的一些问题的解决方案 目录 1.自定义弹框 点击弹框以外地方关闭弹框 2.拦截uni-app的tabbar跳转 1.自定义弹框 点击弹框以外地方关闭弹框 1.声明一个变量 const isDialog ref(false) 2.在根容器里面声明一个蒙版 <view class"network-list-wrapper&q…...

军事目标系列之迷彩作战人员检测数据集VOC+YOLO格式2755张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;2755 标注数量(xml文件个数)&#xff1a;2755 标注数量(txt文件个数)&#xff1a;2755 …...

Qt C++实现马的遍历问题

在这个项目中,我们面对的是一个基于中国象棋的马的遍历问题,使用了C++编程语言,并结合了Qt5库来实现图形界面和棋盘的绘制。以下是这个项目涉及的关键知识点: 马的移动规则:马在象棋中具有独特的“日”字形移动方式,即每次可以向前、后、左或右移动一格,然后在同一行或同…...

node12.22.12在nvm中安装

1、安装nvm 官网&#xff1a;https://nvm.uihtm.com/ 下载&#xff0c;安装 nvm -v 1.2.22、通过 nvm install 12.22.12 安装报错&#xff0c;找不到此版本 通过下载 https://nodejs.org/zh-cn/downloadzip文件 解压 3、查看nvm 安装路径 nvm root4、在目录下新建文件夹 v…...

技术篇-2.3.Golang应用场景及开发工具安装

Golang 虽然语法简洁&#xff0c;上手也较快&#xff0c;但其在高并发、微服务和云原生领域的优势明显&#xff0c;要真正精通并灵活运用仍需积累大量实践经验。与 Java 借助重量级框架不同&#xff0c;Go 倾向于使用标准库和轻量级第三方包来构建高性能、低延迟的系统。 1.1应…...

高效缓存设计的哲学

文章目录 引言基于缓存存储运算结果锁分段散列减小锁粒度异步化提升处理效率原子化避免重复运算小结参考 引言 基于缓存存储运算结果 利用缓存避免非必要的计算&#xff0c;提升结果获取速度&#xff0c;但还是存在问题&#xff0c;每个线程都需要等待锁才能看结果和运算&…...

【生态信息】开源软件全方位解析

开源软件(0pen Source Software&#xff0c;0ss)是指其源代码可以公开发布、查看、使用和修改的软件。这一概念的核心在于开放性和共享性&#xff0c;允许开发者自由地使用、修改、分发以及改进软件。开源软件通常遵循特定的开源许可证&#xff0c;这些许可证确保了软件的自由使…...

FastAPI在 Nginx 和 Docker 环境中的部署

目录 实现示例1. 项目结构2. FastAPI 应用 (app/main.py)3. 依赖文件 (app/requirements.txt)4. Dockerfile5. Nginx 配置 (nginx/nginx.conf)6. Docker Compose 配置 (docker-compose.yml) 使用方法修改代码后更新 实现示例 接下来创建一个简单的示例项目&#xff0c;展示如何…...

计算机网络相关面试题

一、HTTP1.1和HTTP2的区别 HTTP/1&#xff08;主要指 HTTP/1.1&#xff09;和 HTTP/2 是 Web 协议发展中的两个重要版本&#xff0c;二者在性能、协议机制和功能特性上有显著差异。以下从多个维度对比分析&#xff0c;并结合具体案例说明&#xff1a; 一、连接与请求处理方式 1…...

根据当前日期计算并选取上一个月和上一个季度的日期范围,用于日期控件的快捷选取功能

1.选择月份范围 代码如下&#xff1a; <el-date-picker v-model"value" type"monthrange" align"right" unlink-panels range-separator"至"start-placeholder"开始月份" end-placeholder"结束月份" :picker-…...

【C++】set、map 容器的使用

文章目录 1. set 和 multiset 的使用1.1 set类的介绍1.2 set的构造和迭代器1.3 set 的增删查1.4 insert和迭代器调用示例1.5 find和erase使用示例1.6 multiset和set的差异 2. map 和 multimap 的使用2.1 map 类的介绍2.2 pair 类型介绍2.3 map 的构造和迭代器2.4 map 的增删查2…...

【MySQL】第1节|全面理解MySQL架构

快速安装MySQL 使用Docker快速安装mysql8 docker run -d \ --name mysql8 \ --privilegedtrue \ --restartalways \ -p 13306:3306 \ -v /home/mysql8/data:/var/lib/mysql \ -v /home/mysql8/config:/etc/mysql/conf.d \ -v /home/mysql8/logs:/logs \ -e MYSQL_ROOT_PAS…...

YOLOv8模型剪枝笔记(DepGraph和Network Slimming网络瘦身)

文章目录 一、DepGraph剪枝&#xff08;1&#xff09;项目准备1&#xff09;剪枝基础知识2&#xff09;DepGraph剪枝论文解读12&#xff09;DepGraph剪枝论文解读23&#xff09;YOLO目标检测系列发展史4&#xff09;YOLO网络架构 &#xff08;2&#xff09;项目实战&#xff08…...

App Builder技术选型指南:从AI编程到小程序容器,外卖App开发实战

在2025年快速迭代的技术生态中&#xff0c;开发者构建App的路径愈发多样化。本文以开发一个同城外卖App为例&#xff0c;对比当前主流的AI编程工具&#xff08;如Cursor、GitHub Copilot、Trae&#xff09;与小程序容器技术&#xff08;如FinClip&#xff09;的优劣势、难易度及…...

TDengine 高可用——三副本

概述 TDengine 的三副本方案采用 RAFT 算法来实现数据的一致性&#xff0c;包括元数据和时序数据。一个虚拟节点组&#xff08;VGroup&#xff09;构成了一个 RAFT 组&#xff1b;VGroup 中的虚拟节点&#xff08;Vnode&#xff09;&#xff0c;便是该 RAFT 组的成员节点&…...

el-table高度自适应、数据查询后高度展示错误问题

在很多场景中我们需要实现表格的高度自适应&#xff0c;即不同屏幕大小下需要使用不同的高度来设置表格&#xff0c;那么我们应该如何实现呢&#xff1f; 1.el-table实现高度自适应 通过以下代码可以实现表格根据屏幕进行自适应 设置表格的高度 <el-table ref"tableD…...

【蓝桥杯真题精讲】第 16 届 Python A 组(省赛)

文章目录 T1 偏蓝 (5/5)T2 IPv6 (0/5)T3 2025 图形 (10/10)T4 最大数字 (10/10)T5 倒水 (15/15)T6 拼好数 (0/15)T7 登山 (20/20)T8 原料采购 (20/20) 更好的阅读体验 高速访问&#xff1a;https://wiki.dwj601.cn/ds-and-algo/lan-qiao-cup/16th-python-a/永久链接&#xff1…...

Java接口设计:ECharts热力图的绘制

引言 热力图是一种强大的数据可视化工具&#xff0c;通过颜色的深浅变化来直观展示数据密度和分布情况。在现代Web应用中&#xff0c;ECharts作为一款流行的开源数据可视化库&#xff0c;提供了丰富的图表类型&#xff0c;其中热力图因其直观的视觉效果而被广泛使用。本教程将…...

深入理解 MongoDB 的 _id 和 ObjectId:从原理到实践

在 MongoDB 的世界中&#xff0c;_id 字段和 ObjectId 是每个开发者都必须理解的核心概念。作为 MongoDB 文档的唯一标识符&#xff0c;它们不仅影响着数据库的设计&#xff0c;也直接关系到应用的性能和扩展性。本文将全面剖析 _id 和 ObjectId 的工作原理、实际应用场景以及最…...

C++内存复制

C内存复制 方法1 g_savedPoints.resize(pResult->contourData.contourPointCount);//方法1std::copy(pResult->contourData.pointArray, pResult->contourData.pointArray pResult->contourData.contourPointCount, g_savedPoints.begin());方法2 g_savedPoints.r…...

【notepad++如何设置成中文界面呢?】

“Notepad”是一款非常强大的文本编辑软件&#xff0c;将其界面设置成中文的方法如下&#xff1a; 一、工具&#xff0f;原料&#xff1a; 华为 Matebook 15、Windows 10、Notepad 8.4.6。 二 、具体步骤&#xff1a; 1、找到任意一个文本文件&#xff0c;比如 txt 格式的文…...

当AI遇上科研:北大“科学导航”重塑学术探索全流程

在人工智能技术迅猛发展的当下&#xff0c;一场悄然发生的变革&#xff0c;正在改变我们“做科研”的方式。近日&#xff0c;北京大学科学智能研究院联合深势科技&#xff0c;正式上线一款面向科研人员的一体化AI平台——Science Navigator&#xff08;科学导航&#xff09;。这…...

大模型在闭合性胫骨平台骨折诊疗全流程中的应用研究报告

目录 一、引言 1.1 研究背景与目的 1.2 国内外研究现状 1.3 研究方法与创新点 二、大模型预测原理及数据基础 2.1 大模型概述 2.2 数据收集与处理 2.3 模型训练与优化 三、术前预测与方案制定 3.1 骨折类型及损伤程度预测 3.2 手术时机评估 3.3 手术方案制定 3.4 …...