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

2023-10-24 01:20

本文主要是介绍面试题系列第5篇:JDK的运行时常量池、字符串常量池、静态常量池,还傻傻分不清?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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

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

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

JVM运行时内存结构

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

在上图中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文件中都存在。不同的符号信息放置在不同标志的常量表中。
在这里插入图片描述

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

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

支持的类型信息如下:
在这里插入图片描述

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

CONSTANT_Class_info {u1 tag;       //这个值为CONSTANT_Class (7)u2 name_index;//一个index,表示一个索引,引用的是CONSTANT_UTF8_info
}

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];
}

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

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

运行时常量池

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

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

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

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

字符串常量池

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

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

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

常量池内存位置演化

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

在JDK1.7字符串常量池和静态变量被从方法区拿到了堆中,运行时常量池剩下的还在方法区, 也就是hotspot中的永久代。
在这里插入图片描述

在JDK8 hotspot移除了永久代用元空间(Metaspace)取而代之, 这时候字符串常量池还在堆,运行时常量池还在方法区,只不过方法区的实现从永久代变成了元空间(Metaspace)
在这里插入图片描述

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

小结

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

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

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

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

参考文章:

https://blog.csdn.net/qq_31615049/article/details/81611918

https://blog.csdn.net/weixin_43232955/article/details/107411378


程序新视界

公众号“ 程序新视界”,一个让你软实力、硬技术同步提升的平台,提供海量资料

微信公众号:程序新视界

这篇关于面试题系列第5篇:JDK的运行时常量池、字符串常量池、静态常量池,还傻傻分不清?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/271760

相关文章

JAVA中整型数组、字符串数组、整型数和字符串 的创建与转换的方法

《JAVA中整型数组、字符串数组、整型数和字符串的创建与转换的方法》本文介绍了Java中字符串、字符数组和整型数组的创建方法,以及它们之间的转换方法,还详细讲解了字符串中的一些常用方法,如index... 目录一、字符串、字符数组和整型数组的创建1、字符串的创建方法1.1 通过引用字符数组来创建字符串1.2

Linux使用nohup命令在后台运行脚本

《Linux使用nohup命令在后台运行脚本》在Linux或类Unix系统中,后台运行脚本是一项非常实用的技能,尤其适用于需要长时间运行的任务或服务,本文我们来看看如何使用nohup命令在后台... 目录nohup 命令简介基本用法输出重定向& 符号的作用后台进程的特点注意事项实际应用场景长时间运行的任务服

如何在一台服务器上使用docker运行kafka集群

《如何在一台服务器上使用docker运行kafka集群》文章详细介绍了如何在一台服务器上使用Docker运行Kafka集群,包括拉取镜像、创建网络、启动Kafka容器、检查运行状态、编写启动和关闭脚本... 目录1.拉取镜像2.创建集群之间通信的网络3.将zookeeper加入到网络中4.启动kafka集群

C#中字符串分割的多种方式

《C#中字符串分割的多种方式》在C#编程语言中,字符串处理是日常开发中不可或缺的一部分,字符串分割是处理文本数据时常用的操作,它允许我们将一个长字符串分解成多个子字符串,本文给大家介绍了C#中字符串分... 目录1. 使用 string.Split2. 使用正则表达式 (Regex.Split)3. 使用

Java中JSON字符串反序列化(动态泛型)

《Java中JSON字符串反序列化(动态泛型)》文章讨论了在定时任务中使用反射调用目标对象时处理动态参数的问题,通过将方法参数存储为JSON字符串并进行反序列化,可以实现动态调用,然而,这种方式容易导... 需求:定时任务扫描,反射调用目标对象,但是,方法的传参不是固定的。方案一:将方法参数存成jsON字

PostgreSQL如何用psql运行SQL文件

《PostgreSQL如何用psql运行SQL文件》文章介绍了两种运行预写好的SQL文件的方式:首先连接数据库后执行,或者直接通过psql命令执行,需要注意的是,文件路径在Linux系统中应使用斜杠/... 目录PostgreSQ编程L用psql运行SQL文件方式一方式二总结PostgreSQL用psql运

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

如何用Docker运行Django项目

本章教程,介绍如何用Docker创建一个Django,并运行能够访问。 一、拉取镜像 这里我们使用python3.11版本的docker镜像 docker pull python:3.11 二、运行容器 这里我们将容器内部的8080端口,映射到宿主机的80端口上。 docker run -itd --name python311 -p

常用的jdk下载地址

jdk下载地址 安装方式可以看之前的博客: mac安装jdk oracle 版本:https://www.oracle.com/java/technologies/downloads/ Eclipse Temurin版本:https://adoptium.net/zh-CN/temurin/releases/ 阿里版本: github:https://github.com/

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl