Jvm(35),class文件结构----常量池

Class文件格式

  1. 常量池

先了解常量池中需要存放哪些内容,再讨论用什么类来存放这些内容。

  1. 常量池中存放的内容

Class文件中包含常量池,那么我就需要知道常量池会包含哪些内容,接下来才是关心class格式文件用什么类型来存放这些内容。

字面量(Literal)

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

符号引用(Symbolic References)符号引用则属于编译原理方面的概念,包括了下面三类常量:

类和接口的全限定名(Fully Qualified Name)

字段的名称和描述符(Descriptor)方法的名称和描述符

其它:常量池中主要内容是上面2项,说明还有其它内容,这部分内容,在下面我们看到用来描述常量池内容的14种常量项的介绍时就发现标志为15、16、18的常量项类型是用来支持动态语言调用的(jdk1.7时才加入的)。

常量池:可以理解为Class文件之中的资源仓库,它是Class文件结构中与其他项目关联最多的数据类型(后面的很多数据类型都会指向此处),也是占用Class文件空间最大的数据项目之一。

  1. 常量池中为什么要包含这些内容

Java代码在进行Javac编译的时候,并不像C和C++那样有"连接"这一步骤,而是在虚拟机加载Class文件的时候进行动态连接。也就是说,在

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

  1. Class文件中如何描述常量池中内容

知道Class文件的常量池包含的内容后,我们下面就来看看class格式文件使用了哪些类型数据来存放常量池的内容。

由Class文件格式可得紧接着主版本号的是常量池入口。


类型


名称


数量


u2(无符号数)


constant_pool_count


1

     

cp_info(表)


constant_pool


constant_pool_count-1

占用的字节数:2+(constant_pool_count-1)个具体表所占字节。由上表可见,Class文件使用了一个前置的容量计数器

(constant_pool_count)加若干个连续的数据项(constant_pool)的

形式来描述常量池内容。我们把这一系列连续常量池数据称为常量池集合。

先给看一下TestClass.class文件全局的内容,下面就来分析其中常量池中的内容,其它内容后面的文章在分析。从图片也可以看出常量池内容占据了class文件的很大一部分,当然TestClass类中代码比较少就更显得常量池内容的多了。

package com.zlcook.clazz?

public
class
TestClass{ private
int m?

public
int
inc(){

return m+1?

}

}

  1. constant_pool_count

常量池容量计数值(u2类型):从1开始,表示常量池中有多少项常量。即constant_pool_count=1表示常量池中有0个常量项。

设计者将第0项常量空出来是有特殊考虑的,这样做的目的在于满足后面某些指向常量池的索引值的数据在特定情况下需要表达"不引用任何一个常量池项目"的含义,这种情况就可以把索引值置为0来表示。Class 文件结构中只有常量池的容量计数是从1开始,对于其他集合类型,包括接口索引集合、字段表集合、方法表集合等的容量计数都与一般习惯相同,是从0开始的。

TestClass.class文件中constant_pool_count的十进制值为19,表示常量池中有18项常量,索引范围1-18。

  1. constant_pool

constant_pool_count表明了后面有多少个常量项。

14种常量项结构

常量池中每一项常量都是一个表,JDK1.7之后共有14种不同的表结构数据。一个常量池中的每个常量项都逃不脱这14种结构。根据下图每个类型的描述我们也可以知道每个类型是用来描述常量池中哪些内容(主要是字面量、符号引用)的。比如:CONSTANT_Integer_info是用来描述常

量池中字面量信息的,而且只是整型字面量信息。而标志为15、16、18的常量项类型是用来支持动态语言调用的(jdk1.7时才加入的)。

常量池中的14种项目类型

这14种表(或者常量项结构)的共同点是:表开始的第一位是一个u1 类型的标志位(tag),代表当前这个常量项使用的是哪种表结构,即哪种常量类型。

这14种常量项结构还有一个特点是,其中13表占用得字节固定,只有

CONSTANT_Utf8_info占用字节不固定,其大小由length决定。为什么呢?因为从常量池存放的内容可知,其存放的是字面量和符号引用,最终这些内容都会是一个字符串,这些字符串的大小是在编写程序时才确定,比如你定义一个类,类名可以取长取短,所以在没编译前,无法确定大小不固定,编译后,通过utf-8编码,就可以知道其长度。



占用字节


CONSTANT_Class_info


3


CONSTANT_Integer_info


5


CONSTANT_Fieldref_info


5


CONSTANT_Methodref_info


5

  1. 查找testClass.class文件的第一个常量项内容

由上面constant_pool_count得到值为19,因为从1开始计数,所以说明后面有18个常量项,由于每个常量项的表结构都不同但是第一位相同,所以读到第一位就可以确定表结构了。下面我们就来查看第一个常量项包含得内容,至于其它17个常量项内容类似,最后还会介绍java提供得一个工具命令javap来帮我们分析class文件字节码内容。

第一个表的tag为10

由上图可知常量池中第一项常量标志的16进制值是0x0A=10,查表发现这个常量属于CONSTANT_Methodref_info类型,此类型表示类中方法的符号引用。查看该类型的结构如下:

CONSTANT_Methodref_info类型结构

CONSTANT_Methodref_info型常量的第二个数据项为index,类型是u2,index存储的是一个索引值,从class文件中查得该值为 oX0004=4,即它指向常量池中第4个常量;第三个数据项也是索引其值为

0X000F=15,指向常量池种第15个常量。

Paste_Image.png

到此为止,第一个常量项是CONSTANT_Methodref_info型常量项,该类型常量项用来表示类中方法的符号引用,其内容为

tag=10,index1=4,index2=15,因为其表示的是类中方法的符号引用,所以index中存放的不是一个具体得内容,而是一个索引位置,所以说其具体内容存放在另一个常量项中。下面我们就来看看其索引指向的常量项

(即第4个常量项)的内容到底是什么?

找第4个常量项之前需要知道第4个常量项的开始位置,所以需要知道前3个常量项所占字节数。那好就看第2个常量项,由于第一个常量项共占了5个字节,则紧接着的字节就为第二个常量项的tag,如下图可得其值为 0X09=9,说明第2个常量项得项目类型为CONSTANT_Fieldref_info。查表得其该类型得字节长度固定占5个字节。

第二个常量项

依次类推查的第3,4个常量项为CONSTANT_Class_info型。如下图:

前4个常量项

下面就看第四个常量项CONSTANT_Class_info的内容0X070012。

CONSTANT_Class_info存放的是指向类或接口的符号引用。

CONSTANT_Class_info型常量项

根据CONSTANT_Class_info项常量项的结构可知其index数据项又是一个索引项,指向全限定名常量项索引,index数据项的值为0X12=18,表示指向第18 个常量项,根据constant_pool_count的值为19可得,常量池中一共有18个常量项,巧了正好在最后一个,但是要知道18个常量项必须知道前17个常量项所占字节,这里就不一一找了,最后找到第18个常量项CONSTANT_Utf8_info在 class文件中包含的内容如下:

第18个常量项

根据tag等于1得第18项是CONSTANT_Utf8_info型,该类型存储 UTF-8编码的字符串,在TestClass.class文件种该常量项种个数据项的内容如下: length(u2):表示UTF-8编码的字符串占用的字节数,

值为0x0010=16.

bytes(u1):表示长度为length的UTF-8编码的字符串. 因为length=16,所以 length后面紧跟的长度为16个

字节的连续数据是一个使用UTF-8缩略编码表示的字符串。后面紧跟的第一个字节为0x6A=106,那该编码代表的字符为j,我们发现106其实就是字符j对应的ASCII码。后面16个字节代表的字符就是: java/lang/Object

到此为止,我们得到了第一个常量项CONSTANT_Methodref_info的第二个数据项index指向的内容为CONSTANT_Class_info常量项, CONSTANT_Class_info常量的第二个数据项index指向CONSTANT_Utf8_info 常量项,CONSTANT_Utf8_info常量项的内容为 java/lang/Object 。

当然CONSTANT_Methodref_info常量项还有第三个数据项index,其存放的也是一个其他常量的索引。

根据上面的找法我们就可以找出常量池中包含的内容:字面量和符号引用。

  1. 采用javap命令分析class文件

根据上面的找法我们就可以找出常量池中包含的内容:字面量和符号引用。java考虑到这种找法太麻烦了,所以提供了一个命令javap来帮助我们分析class文件的内容。

javap分析class文件用法:javap -verbose class文件名

作者:zlcook

链接:https://www.jianshu.com/p/d8492e748c57

來源:简书简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。上面通过javap命令得到的结果,该结果显示的很友好,由过上面的


第一个常量项


第几个


tag


index


index


最终代表的内容    


class中16

进制值

 
0X0A


0X004


0X000F



转换成10 进制值

 
10


4


15


查完4和15才知道    


javap分析


#1


Methodre


#4


#15


java/lang/Object."<init>":()V    

理论我们可以很清楚的看到常量池一共18项:其中第一项如下:

#1 = Methodref #4.#15 // java/lang/Object."<init>":()V

和我们通过手动方式查看第一个常量项CONSTANT_Methodref_info 对比一下就知道javap显示的内容是多么友好了。

  1. class文件中包含的内容

下面我们来看一下class文件中常量池的内容和java源码中的内容。

TestClass.java代码内容


package com.zlcook.clazz?

public
class
TestClass{

private
int m?

public
int
inc(){

return m+1?

}

}

TestClass.class中常量池内容:

再复习一下常量池中主要存放字面量:如文本字符串、声明为final的常量值等。和符号引用:类和接口的全限定名、字段的名称和描述符、方法的名称和描述符。

所以出现com/zlcook/clazz/TestClass、java/lang/Object、m、inc 都是应该的,那么I、V、、LineNumberTable都是什么?那肯定是字段描述符或者是方法描述符了。这部分是编译时自动生成的,它们会被class文件中其它部分(字段表field_info、方法表method_info、属性表

attribute_info)引用到,它们会用来描述一些不方便使用"固定字节"进行表达的内容。譬如描述方法的返回值是什么?有几个参数?每个参数的类型是什么?因为Java中的"类"是无穷无尽的,无法通过简单的无符号字节来描述一个方法用到了什么类,因此在描述方法的这些信息时,需要引用常量表中的符号引用进行表达。

  1. 哪些字面量会进入常量池中

我们知道class文件存放字面量:如文本字符串、声明为final的常量值等。这里的"等"就挺烦人。

下面我们来看看哪些字面量会进入常量池。(jdk1.8.0环境)

8种基本类型:

上面代码测试结果:

final类型的8种基本类型的值会进入常量池。

非final类型的8种基本类型的值double、float、long的值会进入常量池,包括long_delay_num的值。

StringTest.class的常量池种包含内容:

常量池中包含的字符串类型字面量

原文地址:https://www.cnblogs.com/qingruihappy/p/9691409.html

时间: 2024-11-10 09:15:37

Jvm(35),class文件结构----常量池的相关文章

Jvm(35.2),class文件结构----常量池(下)

NO9.类中引用到的field字段在常量池中是怎样描述的?(CONSTANT_Fieldref_info, CONSTANT_Name_Type_info) 一般而言,我们在定义类的过程中会定义一些 field字段,然后会在这个类的其他地方(如方法中)使用到它.有可能我们在类的方法中只使用field字段一次,也有可能我们会在类定义的方法中使用它很多很多次. 举一个简单的例子,我们定一个叫Person的简单java bean,它有name和age两个field字段,如下所示: package co

Jvm(35.1),class文件结构----常量池(上)

[last updated:2014/11/27] NO1.常量池在class文件的什么位置? 知道了常量池的位置后,然后让我们来揭秘常量池里究竟有什么东西吧- NO2.常量池的里面是怎么组织的? 常量池的组织很简单,前端的两个字节占有的位置叫做常量池计数器(constant_pool_count),它记录着常量池的组成元素 常量池项(cp_info) 的个数.紧接着会排列着constant_pool_count-1个常量池项(cp_info).如下图所示: NO3.常量池项 (cp_info)

JVM内存结构和常量池

1.虚拟机的构成 虚拟结主要由运行时数据区.执行引擎.类加载器三者构成,我们所说的JVM内存模型指的就是运行时数据区. 2.运行时数据区组成和各个区域的作用 运行时数据区可以分为线程共享和线程不共享两部分,其中堆内存和方法区线程共享,本地方法栈.虚拟机栈.程序计数器线程不共享. 2.1.程序计数器 程序计数器(Program Counter Register),也有称作为PC寄存器.想必学过汇编语言的朋友对程序计数器这个概念并不陌生,在汇编语言中,程序计数器是指CPU中的寄存器,它保存的是程序当

JVM常量池理解

在本文描述它们的区别之前,先来了解一下JVM运行时数据区的内存模型. <深入JAVA虚拟机>书中是这样描述的:JVM运行时数据区的内存模型由五部分组成: [1]方法区 [2]堆 [3]JAVA栈 [4]PC寄存器 [5]本地方法栈 对于String s = "haha" ,它的虚拟机指令: 0:   ldc     #16; //String haha     2:   astore_1  3:   return 对于上面虚拟机指令,其各自的指令流程在<深入JAVA虚

Java中的字符串常量池

最近做到一个题目: 问题:String str = new String("abc"),"abc"在内存中是怎么分配的?    答案是:堆,字符串常量区. 题目考查的为Java中的字符串常量池和JVM运行时数据区的相关概念."abc"为字面量对象,其存储在堆内存中.而字符串常量池则存储的是字符串对象的一个引用. Java中的字符串常量池 Java中字符串对象创建有两种形式,一种为字面量形式,如String str = "droid&qu

常量池详细介绍

Java中的字符串常量池详细介绍 郑重声明,本文为转载的 这篇文章主要介绍了Java中的字符串常量池详细介绍,JVM为了减少字符串对象的重复创建,其维护了一个特殊的内存,这段内存被成为字符串常量池或者字符串字面量池,需要的朋友可以参考下 Java中字符串对象创建有两种形式,一种为字面量形式,如String str = "droid";,另一种就是使用new这种标准的构造对象的方法,如String str = new String("droid");,这两种方式我们在

转载:Java中的字符串常量池详细介绍

引用自:http://blog.csdn.net/langhong8/article/details/50938041 这篇文章主要介绍了Java中的字符串常量池详细介绍,JVM为了减少字符串对象的重复创建,其维护了一个特殊的内存,这段内存被成为字符串常量池或者字符串字面量池,需要的朋友可以参考下 Java中字符串对象创建有两种形式,一种为字面量形式,如String str = "droid";,另一种就是使用new这种标准的构造对象的方法,如String str = new Stri

常量池之字符串常量池String.intern()

运行时常量池是方法区(PermGen)的一部分. 需要提前了解: 1. JVM内存模型. 2. JAVA对象在JVM中内存分配 常量池的好处 常量池是为了避免频繁的创建和销毁对象而影响系统性能,其实现了对象的共享. - Java的自动装箱中其实就使用到了运行时常量池.详见:Java 自动装箱与拆箱的实现原理 - 还有字符串常量池. 字符串进入到常量池的两种方法: 1. new String()的实例调用intern()方法. ????执行intern()方法时,若常量池中不存在等值的字符串,JV

第46节:Java当中的常量池

Java当中的常量池 在Java虚拟机jvm中,内存分布为:虚拟机堆,程序计数器,本地方法栈,虚拟机栈,方法区. 程序计数器是jvm执行程序的流水线,是用来存放一些指令的,本地方法栈是jvm操作系统方法所使用的栈,而虚拟机栈是用来执行程序代码的栈,在方法区中有类变量,类信息,方法信息,常量池(符号的引用,以表的形式存在的),堆是虚拟机执行程序代码的所用的堆. 常量?是一旦给定了值就无法改变的量,用final修饰的成员变量为常量. 什么是class文件常量池? 我们知道在class文件中,有类的版