`

Java中的常量池之Class常量池

    博客分类:
  • JVM
JVM 
阅读更多
在Java中,常量池的概念想必很多人都听说过。这也是面试中比较常考的题目之一。在Java有关的面试题中,一般习惯通过String的有关问题来考察面试者对于常量池的知识的理解,几道简单的String面试题难倒了无数的开发者。所以说,常量池是Java体系中一个非常重要的概念。

谈到常量池,在Java体系中,共用三种常量池。分别是字符串常量池、Class常量池和运行时常量池。

本文是《好好说说Java中的常量池》系列的第一篇,先来介绍一下到底什么是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文件的常量池入口处会设置两个字节的常量池容量计数器,记录了常量池中常量的个数。

-w697

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

javap -v  HelloWorld.class
查看常量池内容如下:



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

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

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

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

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

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

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

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

int a = 123;
String s = "hollis";
上面的代码事例中,123和hollis都是字面量。

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

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

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

这也就可以印证前面的常量池中还包含一些com/hollis/HelloWorld、main、([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学习,如果真的有必要,我也可以单独写一篇文章再深入介绍。

参考资料
《深入理解java虚拟机》 《Java虚拟机原理图解》 1.2.2、Class文件中的常量池详解(上)

转自:https://blog.csdn.net/w372426096/article/details/106221798
分享到:
评论

相关推荐

    JVM常量池教程吐血整理干货.md

    Class常量池就存在于Class文件中(Class文件的Constant Pool中)。 Class文件常量池主要存放两大常量:字面量和符号引用。 字面量: 字面量分为文本字符串(如: "abc",1等)和用final修饰的成员变量(实例变量和静态变量)...

    java中常量以及常量池

    1、举例说明 变量 常量 字面...  静态常量池:*.class文件中的常量池,class文件中的常量池不仅仅包含字符串,数值字面量,还包含类、方法的信息,占用class文件绝大部分空间。  运行时常量池:是jvm虚拟机在完成类装

    Java常量池理解与总结

     在Class文件结构中,头的4个字节用于存储魔数Magic Number,用于确定一个文件是否能被JVM接受,再接着4个字节用于存储版本号,前2个字节存储次版本号,后2个存储主版本号,再接着是用于存放常量的常量池,由于...

    class常量池类型分类.pdf

    class常量池类型分类.pdf

    Java常量池解析与字符串intern简介

    在Java应用程序运行时,Java虚拟机会保存一份内部的运行时常量池,它区别于class文件的常量池,是class文件常量池映射到虚拟机中的数据结构。 关于class文件常量池的部分可以参考之前的博文实例探索Class文件。  1...

    RodJohn#jvm#内存区域_运行时常量池1

    常量池静态常量池即*.class文件中的常量池,用于存放字面量和符号引用运行时常量池是jvm运行期间,存储常量的数据结构运行时常量池概念运行时常量池(Runti

    Java class文件格式之常量池_动力节点Java学院整理

    主要为大家详细介绍了Java class文件格式之常量池的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

    JVM执行子系统-JVM进阶

    常量池中常量的数量是不固定的,所以在常量池的入口需要放置一项 u2 类型的数据,代 表常量池容量计数值(constant_pool_count)。与 Java 中语言习惯不一样的是,这个容 量计数是从 1 而不是 0 开始的 常量池中主要...

    Java虚拟机规范(Java SE 7)中文版

    第4章深入分析了用来表示编译后的类和接口的class文件格式,主要包括ClassFile结构、描述符与签名、常量池、字段、方法、属性、代码约束与class文件校验等。第5章定义了Java虚拟机启动以及类和接口的加载、链接和...

    【带书签】Java虚拟机规范(Java SE 7).pdf

    第4章深入分析了用来表示编译后的类和接口的class文件格式,主要包括ClassFile结构、描述符与签名、常量池、字段、方法、属性、代码约束与class文件校验等。第5章定义了Java虚拟机启动以及类和接口的加载、链接和...

    Java7虚拟机规范(高清带目录)

    《Java虚拟机规范(Java SE 7版)》是Java领域最重要和最...第4章深入分析了用来表示编译后的类和接口的class文件格式,主要包括ClassFile结构、描述符与签名、常量池、字段、方法、属性、代码约束与class文件校验等。

    jclasslib.rar_Java 8_java8 下载_java字节码查看工具;_jclasslib.jar_允许修改Jav

    jclasslib是一款免费开源的java字节码...6、增加了CONSTANT_MethodHandle_info和CONSTANT_MethodType_info常量池条目的显示。 7、.class和.jcw文件的文件扩展名处理。 8、将.class和.jcw文件拖动到主窗口将打开它们。

    java虚拟机规范 jdk8.

     深入分析用来表示编译后的类和接口的class文件格式,主要包括ClassFile文件结构、描述符、常量池、字段、方法、属性、格式检查、代码约束与class文件校验等。  定义Java虚拟机启动以及类和接口的加载、链接和初始...

    Core_Java虚拟机规范中文版

    Java虚拟机 内存管理 优化 Class文件 常量池

    Java思维导图

    Java代码在进行Javac编译的时候,并不像C和C++那样有“连接”这一步骤,而是在虚拟机加载Class文件的时候...当虚拟机运行时,需要从常量池获得对应的符号引用,再在类创建时或运行时解析、翻译到具体的内存地址之中。

    自己动手写Java虚拟机 张秀宏 著

    高清非扫描版 带目录 SBN:978-7-111-53413-6 目录 前言 第1章 命令行工具 1.1 准备工作 1.1.1 安装JDK 1.1.2 安装Go 1.1.3 创建目录结构 ...3.3 解析常量池 3.3.1 ConstantPool结构体 3.3.2 ConstantInfo接口

    java虚拟机规范(java SE7)

    第二章:java虚拟结构(运行时区域内存:寄存器,java虚拟机栈,java堆,方法去,运行时常量池,本地方法栈); 第三章:为java虚拟机编译; 第四章:Class文件格式; 第五章:加载、链接与初始化

    JD-GUI,JDGUI,java反编译工具

    java反编译工具: JD-GUI JD-GUI 是一个用 C++ 开发的 Java 反编译工具... (它的作用就是根据class字节码文件,反解析出当前类对应的code区(汇编指令)、本地变量表、异常表和代码行偏移量映射表、常量池等等信息。)

    深入理解Java String#intern()内存模型

    大家知道,Java中string.intern()方法调用会先去字符串常量池中查找相应的字符串,如果字符串不存在,会在字符串常量池中创建该字符串然后再返回。  字符串常量池是一个固定大小的HashMap,桶的数量默认是1009, ...

Global site tag (gtag.js) - Google Analytics