Java中几种常量池面试总结

news2025/1/17 1:17:07

字符串常量池(string pool)

字符串常量池是JVM为了提升性能和减少内存消耗针对字符串(String类)专门开辟的一块区域,主要目的是为了避免字符串的重复创建。

当需要使用字符串时,先去字符串池中查看该字符串是否已经存在,如果存在,则可以直接使用,如果不存在,初始化,并将该字符串放入字符串常量池中。

字符串常量池的位置也是随着JDK版本的不同而位置不同。在JDK1.6及之前,字符串常量池的位置在永久代中,此时字符串常量池中存储的是字符串对象;在JDK1.7时,字符串常量池的位置从永久代移动到了Java堆中,此时,字符串常量池存储的就是字符串对象的引用,具体的实例对象是在堆中开辟的一块空间存放的;在JDK1.8及之后,永久代被元空间取代了。

HotSpot 虚拟机中字符串常量池的实现是 src/hotspot/share/classfile/stringTable.cpp ,StringTable 本质上就是一个HashSet ,容量为 StringTableSize(可以通过 -XX:StringTableSize 参数来设置)。

StringTable 中保存的是字符串对象的引用,字符串对象的引用指向堆中的字符串对象。

注意:在JDK1.7时,静态变量和字符串常量池一起从永久代中移动到了Java堆中。

JDK 1.7 为什么要将字符串常量池移动到堆中?

主要是因为永久代的GC回收效率太低,只有在整堆收集(Full GC)的时候才会被执行GC,而Java程序中通常有大量的被创建的字符串等待回收,将字符串常量池放到堆中,能够更高效及时地回收字符串内存。

class文件中的常量池(class constant pool)

class文件中除了包含类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池(constant pool table),用于存放编译器生成的各种字面量(Literal)和符号引用(Symbolic References)。

在Java源文件被编译成class文件时,其中的所有常量都会被存储在常量池中,而且每个常量在常量池中都有一个唯一编号,可以通过该编号来引用常量池中的常量。

常量池中每一项常量都是一个表,这 14 种表有一个共同的特点:开始的第一位是一个 u1 类型的标志位 -tag 来标识常量的类型,代表当前这个常量属于哪种常量类型。

为什么单独设置class文件常量池

Class文件常量池是为了解决Java虚拟机中内存分配和效率问题而设置的。具体来说,Java虚拟机中的内存是通过JVM在内存中开辟一块特殊区域来管理的,而这个特殊区域叫做方法区(Method Area),其中包含了Java程序中所有的类、接口、字段、方法等信息。而Java程序中的常量通常是在编译时确定的,如果每次程序运行时都需要重新分配内存来存储这些常量,会严重浪费内存资源和降低程序运行效率。

因此,在编译Java源文件时,编译器会将其中的所有常量存储在一个单独的常量池中,然后在运行时将这个常量池加载到内存中,同时将其中的符号引用解析为实际的对象和值。 这种常量池的设计,不仅可以提高内存利用率,还可以加快类加载、解析和执行代码的速度。同时,在运行时,如果需要修改常量池中的常量,只需要修改常量池中的相应项就可以了,无需重新进行内存分配和释放,也就节约了时间和资源。

总之,Java中的常量池设计,既可以提高内存利用率,又可以加快程序的运行速度,在Java程序中有着重要的作用。

字面量

字面量比较接近于 Java 语言层面的的常量概念,如文本字符串、声明为 final 的常量值等。

在计算机编程领域,字面量(literal)是指程序中硬编码的常量值,以及该常量的类型。比如,数字123、字符串"hello world"、true/false布尔值等就是字面量。

字面量通常是编译时就准备好的,程序执行时直接使用,不需要再进行计算或解释,即它们的值可以直接使用或检查。在代码中使用字面量具有以下优点:

  1. 代码可读性好,易于理解:由于字面量代表常量,直接写在程序中,直接阅读代码就可以理解它们代表的值,不需要查找其他地方的定义。

  2. 方便查错:字面量用于定义固定的常量值,可以降低因常量值错误带来的bug和问题。

  3. 编译器优化:编译器可以在编译时就进行一些优化,比如将字面量操作转换成简单的指令,提高程序的运行效率。

总之,字面量在程序中应用广泛,是编程中十分重要的概念。

符号引用

符号引用是一组用来描述所引用目标的符号,属于编译原理方面的概念,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可,主要包括下面几类常量:

  1. 被模块导出或者开放的包(Package)
  2. 类和接口的全限定名(Full Qualified Name)
  3. 字段的名称和描述符(Descriptor)
  4. 方法的名称和描述符
  5. 方法的句柄和方法类型(Method Handle、Method Type、Invoke Dynamic)
  6. 动态调用点和动态常量(Dynamically-Computed Call Site、Dynamically-Computed Constant)

很多资料只提到了2—4,大家可以重点记一下。

在Java虚拟机中,符号引用是一种编译时的符号名称,它是指用来描述类、接口、字段和方法的名称。

简单来说,符号引用就是一个符号名称,比如类名、字段名、方法名等,它并不指向实际的内存地址,在运行时需要通过解析符号引用得到对应的实际内存地址。 换句话说,符号引用就是一种用于在程序中表示对某个类、属性或方法的引用的标记。

符号引用在Java程序中的使用很广泛,因为Java源代码中引用的类、属性或方法可能在编译时并没有被定义,而只在运行时才被加载到内存中。因此,编译器在编译Java源代码时使用符号引用,而在Java虚拟机运行时才根据符号引用解析出对应的实际内存地址,并执行对应的代码。

在Java虚拟机中,由于符号引用不直接指向实际内存地址,因此需要一些机制来解析符号引用,例如类加载器会加载字节码文件到内存中,并将符号引用转化为直接引用,或者JIT编译器在运行时第一次解析符号引用时,将其转换为直接引用。

直接引用

在JVM 类加载过程中,解析阶段,Java虚拟机将常量池内的符号引用替换为直接引用。直接引用可以帮助程序直接定位到所需的对象。

直接引用一般为下面三类:

  1. 直接指向目标的指针
  2. 相对偏移量
  3. 一个能够直接定位到目标的句柄

句柄

句柄是一个是用来标识对象或者项目的标识符,可以用来描述窗体、文件等,值得注意的是句柄不能是常量

偏移量

计算机汇编语言,是指把存储单元的实际地址与其所在段的段地址之间的距离称为段内偏移,也称为“有效地址或偏移量”。

直接引用适合虚拟机的布局相关,同一个符号引用在不同的虚拟机上翻译出来的直接引用一般会不一致。如果有了直接引用,那么引用目标必定已经被加载到了内存当中。

运行时常量池(runtime constant pool)

当Java文件被编译成class文件之后,也就是会生成我上面所说的class常量池,那么运行时常量池又是什么时候产生的呢?

JVM在执行某个类的时候,必须经过加载、连接、初始化,而连接又包括验证、准备、解析三个阶段。而当类加载到内存中后,JVM就会将class常量池中的内容存放到运行时常量池中,由此可知,运行时常量池也是每个类都有一个。在上面我也说了,class文件的常量池中存的是字面量和符号引用,也就是说他们存的并不是对象的实例,而是对象的符号引用值。而经过解析(resolve)之后,也就是把符号引用替换为直接引用,解析的过程会去查询字符串常量池,也就是我们上面所说的StringTable,以保证运行时常量池所引用的字符串与字符串常量池中所引用的是一致的。

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

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

在这里插入图片描述

为什么单独设置运行时常量池

运行时常量池是为了加速Java程序的运行和节省内存资源而设置的。在Java虚拟机中,一些常量是在Java程序运行时动态生成的,如字符串连接表达式、调用方法返回值等。如果每次在程序执行这些操作时都需要重新分配内存来存储常量,会显著降低Java程序的性能。

因此,在运行时常量池中,JVM会将class文件常量池中的符号引用解析为实际的对象和值,并存储在运行时常量池中。这样,当程序需要使用这些常量时,只需要在运行时常量池中查找对应的值即可,而不需要重新分配内存,这样可以加快程序的执行速度,提高Java程序的效率。

另外,由于运行时常量池是每个线程私有的,相比于其他共享区域,如方法区,它更不容易发生线程安全问题。在多线程环境中,如果每个线程都有自己的运行时常量池,能够有效地保障线程安全,避免线程之间的干扰。

总之,运行时常量池的设计提高了Java程序的执行效率,同时也能保障程序的线程安全。在Java程序中,常常用到的字符串、数字等常量值都存储在运行时常量池中,它是Java虚拟机中的重要组成部分之一。

三种常量池之间的关系

在Java虚拟机中,有三种不同类型的常量池:字符串常量池、class文件常量池和运行时常量池。这三种常量池之间存在着紧密的关系。

首先,编译Java源文件时,编译器会将其中的所有字符串字面量和其他编译期声明的常量存储在class文件常量池中。因此,class文件常量池相当于是所有编译时常量的根源。

其次,当Java程序在运行时,JVM会将class文件常量池复制到内存中形成运行时常量池,以供程序在运行时动态使用。运行时常量池是每个线程私有的,用于存储常量池中常量的实际值,同时也存储着类、方法等的相关信息。

最后,字符串常量池是一种特殊的、系统级别的常量池,它用于存储所有字符串字面量的实例,以及其他头文件声明的常量实例,它是在程序运行时被创建。

因此,这三个常量池之间的关系如下:

  • class文件常量池:是所有编译时常量的根源,编译器会将其中的所有字符串字面量和其他编译期声明的常量存储在其中。
  • 运行时常量池:是从class文件常量池中复制得到的,在程序运行时会动态使用其中的常量。作为每个线程私有的内存区域,它存储着常量池中常量的实际值,同时也包含类、方法等的相关信息。
  • 字符串常量池:用于存储所有字符串字面量的实例,以及其他头文件声明的常量实例,它是在程序运行时被创建。

总之,这三个常量池之间相互衔接,紧密联系。在Java程序的编译、加载、解析和执行过程中都发挥着不可替代的

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

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

相关文章

使用vscode 创建vue3.0项目,应用element-plus框架

使用npm指令创建项目 npm init vite-app 项目名称 npm install npm run dev输入http://localhost:3000/ 查看 2、可自定义vue模板 输入vue.json 回车。复制下述代码,然后保存。 {"Print to console": {"prefix": "vue","b…

每天一道算法练习题--Day15 第一章 --算法专题 --- -----------二叉树的遍历

概述 二叉树作为一个基础的数据结构,遍历算法作为一个基础的算法,两者结合当然是经典的组合了。很多题目都会有 ta 的身影,有直接问二叉树的遍历的,有间接问的。比如要你找到树中满足条件的节点,就是间接考察树的遍历…

STM32物联网实战开发(3)——串口打印

串口打印 串口的使用在单片机开发过程中经常出现,因为他在显示数据和调试过程中特别的方便,使用起来也很简单。 1.用STM32CubeMx配置串口 串口1模式选择异步,不开启硬件控制流(串口通信分为同步通信和异步通信,他们往…

云HIS : 电子病历模板制作过程技术经验分享

电子病历的制作就是按照医院机构的特色,根据不同业务需求,使用模板编辑与预览工具, 综合运用工具模块制作个性化、实用化、特色化电子病历模板的过程。 按照制作流程分为以下几个步骤: 1.明确病历类型:根据业务方向…

掌握好这几款TikTok数据分析工具,让你轻松提高曝光率!

为什么别人在TikTok发的普普通通的视频却有那么高的流量、几天内疯狂涨粉,而自己想破脑袋装饰自己的视频,结果却不如人意。 其实原因很简单,TikTok不像国内的抖音只面向中华民族,而是覆盖了150多个国家和75种语言用户&#xff0c…

【五一创作】Scratch资料袋

Scratch软件是免费的、免费的、免费的。任何需要花钱才能下载Scratch软件的全是骗子。 1、什么是Scratch Scratch是麻省理工学院的“终身幼儿园团队”开发的一种图形化编程工具。是面向青少年的一款模块化,积木化、可视化的编程语言。 什么是模块化、积木化&…

【VM服务管家】VM4.x算子SDK开发_3.1 环境配置类

目录 3.1.1 环境配置:CSharp算子SDK开发环境配置方法3.1.2 算子封装:使用C封装算子SDK的方法3.1.3 异常中断:算子SDK软件运行报错“托管调试助手”中断的解决方法3.1.4 深度学习:GPU运行深度学习算子引发StackOverFlow异常的方法 …

FP独立站推广成本太高?那是因为你没看到这篇!

近年来,越来越多的商家开始搭建自己的跨境电商独立站,做起了FP独立站。那么用独立站做FP到底有什么优势?还有,推广成本真的很高吗?今天这期就给大家扒一扒。 用独立站做FP的优势 1、塑造品牌,扩大经营触及…

【HarmonyOS】元服务WebView组件 H5使用localstorage

在日常开发中我们会在应用种接入H5网页,localStorage作为H5本地存储web storage特性的API之一,主要作用是将数据保存在客户端中。对于快速开发元服务,通过WebView组件运行H5如何使用localstorage呢?下文以API7 JavaUI为例为大家做…

k8s 集群搭建详细教程

参考: Kubernetes 文档 / 入门 / 生产环境 / 使用部署工具安装 Kubernetes / 使用 kubeadm 引导集群 / 安装 kubeadm B. 准备开始 一台兼容的 Linux 主机。Kubernetes 项目为基于 Debian 和 Red Hat 的 Linux 发行版以及一些不提供包管理器的发行版提供通用的指令每…

3.3 Linux shell命令(权限、输入输出)

目录 shell shell概述 shell分类 查看当前系统的shell 权限相关命令(也是shell命令) 基本命令 输入输出相关操作 输出命令 输入输出重定向 通配符 管道 历史查询、补齐功能 历史查询 自动补齐 命令置换 shell 什么是shell shell是一种负…

【VM服务管家】VM4.0软件使用_1.2 工具类

目录 1.2.1 文本保存:逐行保存格式化模块输出的方法1.2.2 脚本模块:循环模块搭配脚本使用的方法1.2.3 几何查找:彩色图像的几何查找方法1.2.4 深度学习:图像分割的面积的获取方法1.2.5 颜色识别:使用颜色识别工具做分类…

【Leetcode -86.分隔链表 -92.反转链表Ⅱ】

Leetcode Leetcode -86.分隔链表Leetcode -92.反转链表Ⅱ Leetcode -86.分隔链表 题目:给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。 你应当 保留 两个分区中每…

java数据结构之HashMap

目录 前言 1、初始化 1.1、初始化 1.2、插入第一条数据 2、数组 链表 2.1、插入数据:没有hash冲突 2.2、插入数据:Key不同,但产生hash冲突 2.3、插入数据:Key相同 3、数组 红黑树 3.1、链表如何转化为红黑树? 3.…

Postman测试实践笔记

Postman测试实践 文章目录 Postman测试实践一、Postman安装与使用1.1 Postman下载及安装1.1.2 Postman Mac版 1.2 Postman 更新1.2.1 mac 版更新 1.3 Postman 其他问题 二、网络相关知识2.1 接口2.1.1 软件为什么需要接口 2.2 接口测试2.2.1 什么是接口测试:2.2.2 为…

VTK下载并安装

去官网下载https://vtk.org/download/ 选择最新稳定版本 然后点击source后边的压缩包进行下载。 下载完成后将其解压到特定的文件夹下,然后打开cmake-gui.exe,第一行选择刚刚解压的文件夹,这个文件夹下有一个CMakeLists.txt文件&#xff0c…

【6. 激光雷达接入ROS】

欢迎大家阅读2345VOR的博客【6. 激光雷达接入ROS】🥳🥳🥳 2345VOR鹏鹏主页: 已获得CSDN《嵌入式领域优质创作者》称号👻👻👻,座右铭:脚踏实地,仰望星空&#…

Go | 一分钟掌握Go | 8 - 并发

作者:Mars酱 声明:本文章由Mars酱编写,部分内容来源于网络,如有疑问请联系本人。 转载:欢迎转载,转载前先请联系我! 前言 当今编程界,一个好的编译型语言如果不支持并发&#xff0c…

工控老司机告诉你热电偶和RTD的区别

热电偶和热电阻都是温度传感器,但它们的原理、功能特性和应用场景有所不同。 一、原理区别 首先,热电偶是利用两种不同金属之间的热电效应来测量温度的。其原理是利用温度差引起的金属之间的热电势差进行测量。两种金属之间存在一种热电势(…

Yolov8优化:卷积变体---分布移位卷积(DSConv),提高卷积层的内存效率和速度

论文: https://arxiv.org/pdf/1901.01928v1.pdf 摘要:提出了一种卷积的变体,称为DSConv(分布偏移卷积),其可以容易地替换进标准神经网络体系结构并且实现较低的存储器使用和较高的计算速度。 DSConv将传统的卷积内核分解为两个组件:可变量化内核(VQK)和分布偏移。 通过…