目录

字符串池

字符串池的位置

Class常量池

什么是Class文件

Class常量池

常量池中有什么

字面量

符号引用

Class常量池有什么用

运行时常量池

运行时常量池在JDK各个版本中的实现

运行时常量池中常量的来源

运行时常量池、Class常量池、字符串常量池的区别与联系


字符串池

字符串大家一定都不陌生,他是我们非常常用的一个类。

String作为一个Java类,可以通过以下两种方式创建一个字符串:

String str = "Hollis";String str = new String("Hollis");

而第一种是我们比较常用的做法,这种形式叫做"字面量"。

在JVM中,为了减少相同的字符串的重复创建,为了达到节省内存的目的。会单独开辟一块内存,用于保存字符串常量,这个内存区域被叫做字符串常量池。

当代码中出现双引号形式(字面量)创建字符串对象时,JVM 会先对这个字符串进行检查,如果字符串常量池中存在相同内容的字符串对象的引用,则将这个引用返回;否则,创建新的字符串对象,然后将这个引用放入字符串常量池,并返回该引用。

这种机制,就是字符串驻留或池化。

字符串池的位置

在JDK 7以前的版本中,字符串常量池是放在永久代中的。

因为按照计划,JDK会在后续的版本中通过元空间来代替永久代,所以首先在JDK 7中,将字符串常量池先从永久代中移出,暂时放到了堆内存中。

在JDK 8中,彻底移除了永久代,使用元空间替代了永久代,于是字符串常量池再次从堆内存移动到永久代中

Class常量池

什么是Class文件

在Java代码的编译与反编译那些事儿中我们介绍过Java的编译和反编译的概念。我们知道,计算机只认识0和1,所以程序员写的代码都需要经过编译成0和1构成的二进制格式才能够让计算机运行。

我们在《深入分析Java的编译原理》中提到过,为了让Java语言具有良好的跨平台能力,Java独具匠心的提供了一种可以在所有平台上都能使用的一种中间代码——字节码(ByteCode)。

有了字节码,无论是哪种平台(如Windows、Linux等),只要安装了虚拟机,都可以直接运行字节码。

同样,有了字节码,也解除了Java虚拟机和Java语言之间的耦合。这话可能很多人不理解,Java虚拟机不就是运行Java语言的么?这种解耦指的是什么?

其实,目前Java虚拟机已经可以支持很多除Java语言以外的语言了,如Groovy、JRuby、Jython、Scala等。之所以可以支持,就是因为这些语言也可以被编译成字节码。而虚拟机并不关心字节码是有哪种语言编译而来的。

Java语言中负责编译出字节码的编译器是一个命令是javac

javac是收录于JDK中的Java语言编译器。该工具可以将后缀名为.java的源文件编译为后缀名为.class的可以运行于Java虚拟机的字节码。

如,我们有以下简单的HelloWorld.java代码:

public class HelloWorld {public static void main(String[] args) {String s = "Hollis";}
}

通过javac命令生成class文件:

javac HelloWorld.java

生成HelloWorld.class文件:

如何使用16进制打开class文件:使用 vim test.class ,然后在交互模式下,输入:%!xxd 即可。

可以看到,上面的文件就是Class文件,Class文件中包含了Java虚拟机指令集和符号表以及若干其他辅助信息。

要想能够读懂上面的字节码,需要了解Class类文件的结构,由于这不是本文的重点,这里就不展开说明了。

读者可以看到,HelloWorld.class文件中的前八个字母是cafe babe,这就是Class文件的魔数(Java中的”魔数”)

我们需要知道的是,在Class文件的4个字节的魔数后面的分别是4个字节的Class文件的版本号(第5、6个字节是次版本号,第7、8个字节是主版本号,我生成的Class文件的版本号是52,这时Java 8对应的版本。也就是说,这个版本的字节码,在JDK 1.8以下的版本中无法运行)在版本号后面的,就是Class常量池入口了。

Class常量池

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

由于不同的Class文件中包含的常量的个数是不固定的,所以在Class文件的常量池入口处会设置两个字节的常量池容量计数器,记录了常量池中常量的个数。

当然,还有一种比较简单的查看Class文件中常量池的方法,那就是通过javap命令。对于以上的HelloWorld.class,可以通过

javap -v  HelloWorld.class

查看常量池内容如下:

从上图中可以看到,反编译后的class文件常量池中共有16个常量。而Class文件中常量计数器的数值是0011,将该16进制数字转换成10进制的结果是17。

原因是与Java的语言习惯不同,常量池计数器是从1开始而不是从0开始的,常量池的个数是10进制的17,这就代表了其中有16个常量,索引值范围为1-16。

常量池中有什么

介绍完了什么是Class常量池以及如何查看常量池,那么接下来我们就要深入分析一下,Class常量池中都有哪些内容。

常量池中主要存放两大类常量:字面量(literal)和符号引用(symbolic references)。

字面量

前面说过,Class常量池中主要保存的是字面量和符号引用,那么到底什么字面量?

在计算机科学中,字面量(literal)是用于表达源代码中一个固定值的表示法(notation)。几乎所有计算机编程语言都具有对基本值的字面量表示,诸如:整数、浮点数以及字符串;而有很多也对布尔类型和字符类型的值也支持字面量表示;还有一些甚至对枚举类型的元素以及像数组、记录和对象等复合类型的值也支持字面量表示法。

以上是关于计算机科学中关于字面量的解释,并不是很容易理解。说简单点,字面量就是指由字母、数字等构成的字符串或者数值。

字面量只可以右值出现,所谓右值是指等号右边的值,如:int a=123这里的a为左值,123为右值。在这个例子中123就是字面量

int a = 123;
String s = "hollis";

上面的代码事例中,123和hollis都是字面量。

本文开头的HelloWorld代码中,Hollis就是一个字面量。

符号引用

常量池中,除了字面量以外,还有符号引用,那么到底什么是符号引用呢。

符号引用是编译原理中的概念,是相对于直接引用来说的。主要包括了以下三类常量: * 类和接口的全限定名 * 字段的名称和描述符 * 方法的名称和描述符

这也就可以印证前面的常量池中还包含一些com/hollis/HelloWorldmain([Ljava/lang/String;)V等常量的原因了。

Class常量池有什么用

前面介绍了这么多,关于Class常量池是什么,怎么查看Class常量池以及Class常量池中保存了哪些东西。有一个关键的问题没有讲,那就是Class常量池到底有什么用。

首先,可以明确的是,Class常量池是Class文件中的资源仓库,其中保存了各种常量。而这些常量都是开发者定义出来,需要在程序的运行期使用的。

在《深入理解Java虚拟》中有这样的表述:

Java代码在进行Javac编译的时候,并不像C和C++那样有“连接”这一步骤,而是在虚拟机加载Class文件的时候进行动态连接。也就是说,在Class文件中不会保存各个方法、字段的最终内存布局信息,因此这些字段、方法的符号引用不经过运行期转换的话无法得到真正的内存入口地址,也就无法直接被虚拟机使用。当虚拟机运行时,需要从常量池获得对应的符号引用,再在类创建时或运行时解析、翻译到具体的内存地址之中。关于类的创建和动态连接的内容,在虚拟机类加载过程时再进行详细讲解。

前面这段话,看起来很绕,不是很容易理解。其实他的意思就是: Class是用来保存常量的一个媒介场所,并且是一个中间场所。在JVM真的运行时,需要把常量池中的常量加载到内存中。

至于到底哪个阶段会做这件事情,以及Class常量池中的常量会以何种方式被加载到具体什么地方,会在本系列文章的后续内容中继续阐述。欢迎关注我的博客(http://www.hollischuang.com) 和公众号(Hollis),即可第一时间获得最新内容。

另外,关于常量池中常量的存储形式,以及数据类型的表示方法本文中并未涉及,并不是说这部分知识点不重要,只是Class字节码的分析本就枯燥,作者不想在一篇文章中给读者灌输太多的理论上的内容。感兴趣的读者可以自行Google学习,如果真的有必要,我也可以单独写一篇文章再深入介绍。

运行时常量池

运行时常量池( Runtime Constant Pool)是每一个类或接口的常量池( Constant_Pool)的运行时表示形式。

它包括了若干种不同的常量:从编译期可知的数值字面量到必须运行期解析后才能获得的方法或字段引用。运行时常量池扮演了类似传统语言中符号表( SymbolTable)的角色,不过它存储数据范围比通常意义上的符号表要更为广泛。

每一个运行时常量池都分配在 Java 虚拟机的方法区之中,在类和接口被加载到虚拟机后,对应的运行时常量池就被创建出来。

以上,是Java虚拟机规范中关于运行时常量池的定义。

运行时常量池在JDK各个版本中的实现

根据Java虚拟机规范约定:每一个运行时常量池都在Java虚拟机的方法区中分配,在加载类和接口到虚拟机后,就创建对应的运行时常量池。

在不同版本的JDK中,运行时常量池所处的位置也不一样。以HotSpot为例:

在JDK 1.7之前,方法区位于堆内存的永久代中,运行时常量池作为方法区的一部分,也处于永久代中。

因为使用永久代实现方法区可能导致内存泄露问题,所以,从JDK1.7开始,JVM尝试解决这一问题,在1.7中,将原本位于永久代中的运行时常量池移动到堆内存中。(永久代在JDK 1.7并没有完全移除,只是原来方法区中的运行时常量池、类的静态变量等移动到了堆内存中。)

在JDK 1.8中,彻底移除了永久代,方法区通过元空间的方式实现。随之,运行时常量池也在元空间中实现。

运行时常量池中常量的来源

运行时常量池中包含了若干种不同的常量:

编译期可知的字面量和符号引用(来自Class常量池) 运行期解析后可获得的常量(如String的intern方法)

所以,运行时常量池中的内容包含:Class常量池中的常量、字符串常量池中的内容

运行时常量池、Class常量池、字符串常量池的区别与联系

虚拟机启动过程中,会将各个Class文件中的常量池载入到运行时常量池中。

所以, Class常量池只是一个媒介场所。在JVM真的运行时,需要把常量池中的常量加载到内存中,进入到运行时常量池。

字符串常量池可以理解为运行时常量池分出来的部分。加载时,对于class的静态常量池,如果字符串会被装到字符串常量池中。


学习Java——字符串池、常量池相关推荐

  1. java字符串的常量池

    1.对于引用类型来说,==进行的是地址值的比较. 2.双引号直接写的字符串在常量池当中,new的不在池当中.

  2. 好好说说Java中的常量池之Class常量池

    前言 在Java中,常量池的概念想必很多人都听说过.这也是面试中比较常考的题目之一.在Java有关的面试题中,一般习惯通过String的有关问题来考察面试者对于常量池的知识的理解,几道简单的Strin ...

  3. 好好说说Java中的常量池之Class常量池 1

    转载自   好好说说Java中的常量池之Class常量池 在Java中,常量池的概念想必很多人都听说过.这也是面试中比较常考的题目之一.在Java有关的面试题中,一般习惯通过String的有关问题来考 ...

  4. Java当中的常量池

    本文转载公众号  达叔与他的朋友们 Java当中的常量池 在Java虚拟机jvm中,内存分布为:虚拟机堆,程序计数器,本地方法栈,虚拟机栈,方法区. 程序计数器是jvm执行程序的流水线,是用来存放一些 ...

  5. java 为什么需要常量池 1

    转载自  java 为什么需要常量池 java中讲的常量池,通常指的是运行时常量池,它是方法区的一部分,一个jvm实例只有一个运行常量池,各线程间共享该运行常量池. java内存模型中将内存分为堆和栈 ...

  6. 第46节:Java当中的常量池

    Java当中的常量池 在Java虚拟机jvm中,内存分布为:虚拟机堆,程序计数器,本地方法栈,虚拟机栈,方法区. 程序计数器是jvm执行程序的流水线,是用来存放一些指令的,本地方法栈是jvm操作系统方 ...

  7. stringbuilder调用tostring常量池_彻底弄懂java中的常量池

    作者:tracy_666链接:https://www.jianshu.com/p/55f65dac1b4b JVM常量池主要分为Class文件常量池.运行时常量池,全局字符串常量池,以及基本类型包装类 ...

  8. Java中整数常量池的概念

    Java中整数常量池的概念: java中为了提高程序的执行效率,将[-128, 127]之间256个整数所有的包装对象提前创建好了,类加载时就已经创好了,放在了一个方法区的"整数常量池&qu ...

  9. Java中的常量池(字符串常量池、class常量池和运行时常量池)

    简介: 这几天在看Java虚拟机方面的知识时,看到了有几种不同常量池的说法,然后我就去CSDN.博客园等上找资料,里面说的内容真是百花齐放,各自争艳,因此,我好好整理了一下,将我自认为对的理解写下来与 ...

最新文章

  1. 【亲测可用】Xshell可视化配置一个登录服务器后自动进入指定目录的方法
  2. django创建项目案例1详细介绍方法01
  3. Kafka文件存储机制及offset存取
  4. leetcode 226. Invert Binary Tree
  5. SQL语句对于NUll的筛选
  6. jquery遍历集合数组标签
  7. 一个关于WCF调用远程链接返回405错误不允许使用此方法的问题
  8. codeup 1128: 出租车费 贪心|找规律
  9. 如何及时还原被删除的活动目录对象
  10. 在aarch64-himix100-linux-gcc下cmake报错(The C compiler identification is unknown)
  11. 基于密集卷积神经网络的多类植物病害检测与分类
  12. jQuery操作数组的工具方法
  13. 探究Ptcms小说采集规则
  14. 《单片机原理与接口技术》小结
  15. win10如何打开摄像头_如何解决:Win10打开软件报错“应用程序无法正常启动 0xc0150002”...
  16. 如何学习数字集成电路:数字IC必读书籍
  17. 解决OneNote for Windows 10 不能打开onedrive上已有笔记本问题
  18. win10卸载ie11后安装不上
  19. 腾讯云副总裁王龙:云+AI是产业升级的最佳搭档
  20. Java 算法之三色旗

热门文章

  1. MapReduce:Map端Join算法实现
  2. uLab Systems在中国上海开设办事处以支持全球扩张
  3. 大脑中的不同网络:突显网络、听觉网络、基底神经节网络、高级视觉网络、视觉空间网络、默认模式网络、语言网络、执行网络、楔前叶网络、初级视觉网络、感觉运动网络
  4. 脑电信号处理——加入通道注意力(利用可训练张量)
  5. 阿里云MVP:开发者的超能力,用技术创造更好世界...
  6. 在excel中如何筛选重复数据_Excel表格技巧—如何在 Excel 中查找重复值
  7. 学计算机办公室软件使用知识,办公电脑软件的常用技巧
  8. Java/java程序设计多态参数:练手小题:父类员工类含有普通员工和经理两个子类,普通员工有work方法,经理有特有属性年终奖,和manage方法现要求在主文件中设计能调用所有类的年薪方法和工作方法
  9. 使用 jsPDF- Autotable 将 HTML 表格导出为 PDF
  10. 敏捷思维- 架构设计中的方法学