面试题系列第5篇:JDK的运行时常量池、字符串常量池、静态常量池,还傻傻分不清?

简介: 面试题系列第5篇:JDK的运行时常量池、字符串常量池、静态常量池,还傻傻分不清?

《Java面试题系列》:一个长知识又很有意思的专栏。深入挖掘、分析源码、汇总原理、图文结合,打造公众号系列文章,面试与否均可提升Level。欢迎持续关注【程序新视界】。本篇为第5篇。


【番外篇】本篇核心:JDK各个版本中JDK的运行时常量池、字符串常量池、静态常量池的功能及存储位置。


在写本系列文章时,发现一旦追究起底层实现都会涉及到一些内存结构的问题。其中涉及比较多的便是常量池,本篇文章汇总一下JDK的运行时常量池、字符串常量池、静态常量池的功能及存储结构。


JVM运行时内存结构

在了解常量池之前我们先通过一张图了解一下JVM的整个内存分布图。下图为JDK7的内存结构:


image.png在上图中JVM所管理的内存主要包括以下区域:程序计数器(Program Counter Register)、虚拟机栈(VM Stack)、本地方法栈(Native Method Stack)、方法区(Method Area)、堆(Heap)。


不同版本的JVM的内存结构有不同的变化,这些变化对我们今天要讲的三个概念会有所影响,后面我们会逐一讲解。


了解了JVM内存结构,那么运行时常量池、字符串常量池、静态常量池对于的都位于JVM的什么区域呢?先来看看它们的定义及功能。


静态常量池

Java程序要运行时,需要编译器先将源代码文件编译成字节码(.class)文件,然后在由JVM解释执行。


class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池(Constant pool table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入运行时常量池中存放。


静态常量池就是上面说的class文件中的常量池。class常量池是在编译时每个class文件中都存在。不同的符号信息放置在不同标志的常量表中。


image.png常量池中存放的符号信息,在JVM执行指令时需要依赖使用。常量池中的所有项都具有如下通用格式:


cp_info {

   u1 tag;     //表示cp_info的单字节标记位

   u1 info[];  //两个或更多的字节表示这个常量的信息,信息格式由tag的值确定

}

1

2

3

4

支持的类型信息如下:


image.png以CONSTANT_Class为例,它用于表示类或者接口,格式如下:


CONSTANT_Class_info {

u1 tag;       //这个值为CONSTANT_Class (7)

u2 name_index;//一个index,表示一个索引,引用的是CONSTANT_UTF8_info

}

1

2

3

4

CONSTANT_Class_info类型是由一个tag和一个name_index组成。name_index中的index表示它是一个索引,引用的是CONSTANT_UTF8_info。


CONSTANT_Utf8_info用于表示字符常量的值,结构如下所示:


CONSTANT_Utf8_info {

u1 tag;

u2 length;

u1 bytes[length];

}

1

2

3

4

5

tag表示为:CONSTANT_Utf8(1);length指明了bytes[]数组的长度;bytes[]数组引用了上一个length作为其长度。字符常量采用改进过的UTF-8编码表示。


对于静态常量池我们需要知道它存在于编译器,如果说与运行时有关的话,可以说运行时中的常量是JVM加载class文件之后进行分配的。


运行时常量池

运行时常量池就是将编译后的类信息放入方法区中,也就是说它是方法区的一部分。


运行时常量池用来动态获取类信息,包括:class文件元信息描述、编译后的代码数据、引用类型数据、类文件常量池等。


运行时常量池是在类加载完成之后,将每个class常量池中的符号引用值转存到运行时常量池中。每个class都有一个运行时常量池,类在解析之后将符号引用替换成直接引用,与全局常量池中的引用值保持一致。


运行时常量池相对于class文件常量池的另外一个特性是具备动态性,java语言并不要求常量一定只有编译器才产生,也就是并非预置入class文件中常量池的内容才能进入方法区运行时常量池,运行期间也可能将新的常量放入池中。


字符串常量池

字符串池里的内容是在类加载完成,经过验证、准备阶段之后存放在字符串常量池中。关于字符串常量池的具体实现我们这里先不展开,后面用专门的文章来进行讲解。


字符串常量池的处理机制我们前面文章已经讲到,只会存储一份,被所有的类共享。基本流程是:创建字符串之前检查常量池中是否存在,如果存在则获取其引用,如果不存在则创建并存入,返回新对象引用。


字符串常量池随着JDK版本的演化所在的位置也在不断的变化,下面我们会专门用图讲解一下。


常量池内存位置演化

在JDK1.7之前运行时常量池逻辑包含字符串常量池存放在方法区, 此时hotspot虚拟机对方法区的实现为永久代。

image.pngimage.pngimage.png通过上面的图解,我们可以轻易得知在不同的版本中方法区及内部组成部分是在不断变化的。


小结

通过本篇文章我们针对JDK的运行时常量池、字符串常量池、静态常量池进行逐一讲解,同时针对不同版本的JVM(hotspot虚拟机)中它们所处的内存位置进行了图解。


总结一下就是:静态变量处于编译器,存在于class文件内,可通过javap verbose命令查看字符串合并时查看的是静态常量池里面的内容;字符串常量池曾经属于运行时常量池的一部分,位于方法区,但随着JVM版本的演变,二者已经分开。在JDK8以后字符串常量池位于堆中,而运行时常量池位于方法区。


其实关于此部分的内容还有很多,特别是字符串常量池,欢迎大家持续关注。后续会对字符串常量池再进行针对性的分析。在此过程中也发现很多文章在没有指定JDK版本的情况下进行描述,都有失偏驳。本系列为大家考证,去伪存真。



目录
相关文章
|
XML API 数据格式
天气预报API接口
原文:天气预报API接口 一、中央气象台API接口: 1.
19063 0
|
3月前
|
缓存 数据库连接 数据库
缓存三剑客(穿透、击穿、雪崩)
缓存穿透指查询数据库和缓存中都不存在的数据,导致请求直接冲击数据库。解决方案包括缓存空对象和布隆过滤器。缓存击穿是大量请求访问同一个失效的热点数据,使数据库瞬间压力剧增,解决方法有提前预热、设置永不过期、加锁限流等。缓存雪崩是大量key同时失效,导致所有请求直达数据库,可通过引入随机过期时间缓解。三者分别对应单点爆破、全面崩塌等问题,需根据场景选择合适策略优化系统性能与稳定性。
226 0
|
4月前
|
消息中间件 存储 Kafka
一文带你从入门到实战全面掌握RocketMQ核心概念、架构部署、实践应用和高级特性
本文详细介绍了分布式消息中间件RocketMQ的核心概念、部署方式及使用方法。RocketMQ由阿里研发并开源,具有高性能、高可靠性和分布式特性,广泛应用于金融、互联网等领域。文章从环境搭建到消息类型的实战(普通消息、延迟消息、顺序消息和事务消息)进行了全面解析,并对比了三种消费者类型(PushConsumer、SimpleConsumer和PullConsumer)的特点与适用场景。最后总结了使用RocketMQ时的关键注意事项,如Topic和Tag的设计、监控告警的重要性以及性能与可靠性的平衡。通过学习本文,读者可掌握RocketMQ的使用精髓并灵活应用于实际项目中。
2389 9
 一文带你从入门到实战全面掌握RocketMQ核心概念、架构部署、实践应用和高级特性
|
10月前
|
存储 Java
Bitmap位图(Java实现)
本文介绍了使用Java实现一个简单的Bitmap,通过自定义byte数组存储数据,提供put和exist方法分别用于插入数据和查询数据是否存在。Bitmap利用位操作高效地管理大量布尔值,适用于空间优化的场景。代码中详细解释了位图的核心原理、方法实现及边界检查。后续计划探讨位图在海量数据去重中的应用及JDK BitSet源码分析。
675 7
|
9月前
|
消息中间件 存储 缓存
一文带你秒懂 Kafka工作原理!
Apache Kafka 是一个高吞吐量、低延迟的分布式消息系统,广泛应用于实时数据处理、日志收集和消息队列等领域。它最初由LinkedIn开发,2011年成为Apache项目。Kafka支持消息的发布与订阅,具备高效的消息持久化能力,适用于TB级数据的处理。
|
12月前
|
缓存 Java 数据库连接
使用MyBatis缓存的简单案例
MyBatis 是一种流行的持久层框架,支持自定义 SQL 执行、映射及复杂查询。本文介绍了如何在 Spring Boot 项目中集成 MyBatis 并实现一级和二级缓存,以提高查询性能,减少数据库访问。通过具体的电商系统案例,详细讲解了项目搭建、缓存配置、实体类创建、Mapper 编写、Service 层实现及缓存测试等步骤。
126 2
|
12月前
|
Java 调度
HashMap为什么会死循环?
本文分析了在Java中HashMap导致死循环的原因,主要由于在JDK 1.7及之前版本中,多线程环境下进行扩容操作时,头插法导致的链表反转,以及线程调度问题,从而形成循环链表。
449 0
HashMap为什么会死循环?
|
存储 Java 编译器
【面试题精讲】JVM-方法区-运行时常量池
【面试题精讲】JVM-方法区-运行时常量池
|
Java Spring 容器
什么情况下会导致@Async异步方法会失效?
什么情况下会导致@Async异步方法会失效?
219 1
|
存储 Java 测试技术
解析 -XX:+UseCompressedOops JVM 选项
【8月更文挑战第21天】
247 0