JVM——class文件结构 详解

目录

1. Class文件结构简介

2、常量池

2.1 UTF-8 编码的字符串 CONSTANT_Utf8_info

2.2 整数 CONSTANT_Integer_info 

2.3 类信息 CONSTANT_Class_info

2.4 字符串字面量 CONSTANT_String_info

2.5 字段引用信息 CONSTANT_Fieldref_info

2.6 方法引用信息 CONSTANT_Methodref_info

2.7 接口方法引用信息 CONSTANT_InterfaceMethodref_info

2.8 名称及类型信息 CONSTANT_NameAndType

3、访问标志Access_flag

4. 类索引(This_class),父类索引(Super_class),接口索引集合(Interfaces)

5、字段集合 fields[ count ]

6、方法集合 methods[ count ]

7、属性表集合

1. Class文件结构简介

不管是大端模式还是小端模式,cpu在内存的读写数据都是从低地址开始到高地址; 大端模式就是先读到的是高位(低地址存高位,高地址存低位),小端模式就是先读到的是低位(低地址存低位,高地址存高位)。

大端小端产生的根本原因:在计算机中,内存之间不能之间传输数据,需要通过寄存器转,内存的存储单元是字节,但是寄存器是多字节的,于是就有了高位、低位之分,不同的厂家生产的CPU的标准不同,有的用大端(绝大多数),有的用小端,大端小端也就随之而来。

JDK的bin目录下,有个javap的工具,可以在命令行窗口中利用此工具显示出Class文件的结构,便于分析class字节码文件。

class文件结构简介:

class文件的结构就如上图所示,固定顺序,固定格式,以字节为单位,字节之间不存在任何空隙,对于超过8位的数据,将高位放在class文件的前面,低位放在后面(这是大端模式),否则jvm无法正确解析class文件。

 magic:魔数,u4表示4个字节,就是一个文件类型的标志,表明此文件是哪种类型,png、TXT,等等,java class文件的魔数固定是0xCAFEBABE,虚拟机加载class文件时,会先检查这个部分,如果不是这个值,虚拟机拒绝加载该文件。

minor_version:次版本号;

major_version:主版本号; 主次版本号构成版本号,class文件的版本号对应着jdk的版本号,比如:45(1.1)、46(1.2)、47(1.3)、48(1.4)、49(1.5)、50(1.6)、51(1.7),可以看到jdk版本每增加0.1,版本号就加1,低版本JDK编译生成的class文件,可以被高版本的JRE执行,但是反之,则不行,虚拟机加载class文件之前,会先查看class文件的版本是否在自己的支持范围之内。

constant_pool_count:常量池的大小,从1开始计数,比如count = 11,那么元素共10个,1~10,第0个单元空出来;

constant_pool:常量池,是数组形式,由字面常量 和 符号引用 组成,保存字面常量和符号引用,符号引用保存的是引用的全局限定名,所以保存的是字符串;

access_flags:当前类的访问权限;

this_class:当前类的全局限定名在常量池中的索引(常量池以数组方法存储,索引即为下标);

super_class:当前类的父类的全局限定名在常量池中的索引;

interfaces_count:当前类实现的接口数目;

interfaces[ interfaces_count ]:这些接口的全局限定名在常量池中的索引的数组;

fields_count:字段的数量;

fields[ fields_count ]:

结构图如下:


重点说明:

 用下面这个类的代码作为分析的例子:


 2、常量池

常量池主要存放字面量 和 符号引用,符号引用包括:类和接口的全限定名、字段的名称和描述符、方法的名称和描述符。

常量池的存储形式可理解为数组,每个元素又是一个结构(标识(u1类型,一个字节),元素内容),每个元素都有一个索引值,通过这个索引值就可以定位数组中的某个元素,元素的类型有很多,通过标识位来区分,如下表(可以理解常量池主要存储字面量和符号引用了吧!):


上表是元素类型表,常量池每个元素的具体结构如下表,下表中那些引用结构中,index为索引,索引就是常量池中的数组下标。


好,开始分析Test类的class文件(下面用16进制表示)中的常量池内容:

0700 02 01 00 0454 65 73 74 但看这段,07代表类或者接口的符号引用,index占2字节,值为2,代表这是类,不是接口,接着 01代表字符串,length为04,后面4个字节为字符串内容0x54657374 编码过来就是“Test” 。这句 class Test。

其它的类似这样。

我们一个一个来分析每种类型。

2.1 UTF-8 编码的字符串 CONSTANT_Utf8_info

下图是它的数据结构,第一个是类型标识(1个字节),第二个是这个字符串的长度是多少字节(2个字节),因此变量名、方法名、类名、接口名、字符串常量的最大长度不能超过65535个字节,因为u2能表示的最大数就是65535 bytes = 64KB - 1byte,第三个参数就是字符串的内容了。

2.2 整数 CONSTANT_Integer_info 

第一个参数是类型标识(1个字节),如果虚拟机读到类型是3,就知道它是Int类型,默认读取随后的4个字节,第二参数就是int值(4个字节)。


Float、Long、Double类型的都是一个道理。

2.3 类信息 CONSTANT_Class_info

用于描述类名或者接口名,第一个参数是类型标识(1个字节),第二参数是常量池索引,指向第几个常量项,这个常量项存的就是类名(其实就是utf-8字符串常量来存储类名)。


2.4 字符串字面量 CONSTANT_String_info

用于描述字符串字面量,比如“heheh”,第一个参数是类型标识(1个字节),第二个参数是常量池索引,指向的常量项存储着字符串字面量的全限定名(还是UTF-8字符串常量来存储的)。


2.5 字段引用信息 CONSTANT_Fieldref_info

用于描述类的成员变量的引用信息,第一个参数是类型标识,第二个参数是索引,指向自己的类的CONSTANT_Class_info常量项,第三个参数是索引,指向名称及类型描述符CONSTANT_NameAndType常量项。


2.6 方法引用信息 CONSTANT_Methodref_info

用于描述类的成员方法的引用信息,第一个参数同上,第二参数是索引,指向自己的类的CONSTANT_Class_info常量项,第三个参数是索引,指向名称及类型描述符CONSTANT_NameAndType常量项。


2.7 接口方法引用信息 CONSTANT_InterfaceMethodref_info

用于描述接口的成员方法的引用信息,第一个参数同上,第二参数是索引,指向自己的接口的CONSTANT_Class_info常量项,第三个参数是索引,指向名称及类型描述符CONSTANT_NameAndType常量项。



2.8 名称及类型信息 CONSTANT_NameAndType

用于描述某个内容的名称和类型的信息,第一个参数同上,第二个参数是索引,指向存储该内容的名称的常量项(utf-8常量项),第三个参数是索引,指向该内容的类型描述的常量项(utf-8常量项)。


3、访问标志Access_flag

常量池结束后就是访问标志的内容,2个字节,这个标志用于表示一个类或者接口的访问信息,包括:这个是类还是接口,是否定义为public类型,是否定义为abstract类型,是否被声明为final等,各种标志的值如下:


这些值是16进制表示的,仔细想,占据32位,每一种标志指示占用了其中一个bit(位)。

所有如果这个类或者接口同时又上表中的多个属性,那么只需将相应的bit置为1即可。

4. 类索引(This_class),父类索引(Super_class),接口索引集合(Interfaces)

This_class 是一个2字节的索引,代表本类(接口)的全限定名,指向的是常量池中CONSTANT_Class_info。

Super_class 是一个2字节的索引,代表本类的父类,仅仅是父类,指向常量池中CONSTANT_Class_info,因为java类最多只有1个父类,没有继承某个类的话,默认就是Object类,如果本类是个接口,那么这个Super_class为0,因为接口是不可能有父类的,接口只能有父接口。

Interfaces 是接口集合,用于表示一个类实现了哪些接口,或者一个接口继承了哪些接口,分为两部分:interfaces_count 接口数量(2个字节)、interfaces[ ] 接口数组(存的就是那些接口),如果接口数量为0,那么后面就没有接口数组了。

5、字段集合 fields[ count ]

字段集合,我习惯叫做字段数组,用于描述接口或者类中的变量,包括类级变量或者实例级变量,不包括在方法内声明的变量。可以描述的信息有:变量的作用域(public、private、protected)、是类级变量还是实例级变量(static 修饰符)、可变性(final)、并发可见性(volatile)、可否序列化(transient)、变量数据类型(基本类型、对象、数组)、变量名称。

字段集合中不会列出从超类、父接口中继承来的变量,但是一个内部类的字段集合中会自动添加使用到外部类的那些变量。

fields_count 表示变量的数量,随后就存放多个变量的信息,类似数组一样,变量是有结构的,结构如下表:


access_flags:和字节码文件中的Access_flags非常类似,用于表示一个变量的访问属性(public、protected、private、static等等),如下表:每个标志类型占据32位中的一位,拥有什么访问属性,就将该bit置为1。


name_index:变量的简单名字(不是全局限定名那种,就是名字而已,比如int aa = 5;简单名字就是aa,void ss(){ },简单名字就是ss)的索引,索引就是常量池的下标,变量的简单名字存在常量池;

descriptor_index:变量的描述符的常量池索引,(描述符就是描述变量的数据类型、方法的参数列表、方法的返回值)此处就是变量的数据类型,变量的数据类型也存储在常量池中;这些基本数据类型都用一个大写字符来表示,只有对象类型是用大写字符L加上对象的全限定名加上分号来表示,具体如下表:


数组类型如何表示呢?是几维数组,就在前面加几个“ [ ”符号,比如 double[ ]  aaa; 那么在常量池中存的描述符就是 [D,  String[ ][ ]  bbb;那么常量池中的描述符就是[[Ljava/lang/String。   

attributes_count:属性表的属性(额外信息)的数量;

attributes[ attributes_count ]:属性表,每个元素又是一个结构(这个就复杂了,本文没给出详解);

6、方法集合 methods[ count ]

方法集合和字段集合非常类似,基本一样,只是描述符和access_flags的内容不一样,方法的描述符按照参数列表、返回值的顺序描述,方法的参数按照顺序放在“()”里,返回值的类型就放在括号后面,比如 void aaa() 的描述符“()V”,int hehe(char[] aa, String bbb) 的描述符是“([CLjava/lang/String)V”。

Test类中显示定了两个方法,但是class文件中的methods_count =4 ;因为编译器默认为Test类添加了void <clinit>()方法,这个方法是初始化静态变量和静态块的,还添加了一个默认的无参构造函数,所以methods_count = 4。


access_flag的信息如下:


那方法里的java代码在class字节码文件里存在哪里呢?存储在属性表attributes里一个名为“Code”的属性里。

注意:java语言里,对于方法,只有返回值类型不同的话,不能称为重载,会报错,但是在class字节码文件中,只要方法的描述符不相同,这些方法就能共存,换句话说,就是即便两个方法只有返回值类型不同,也能在class字节码文件中共存。

7、属性表集合

class文件有属性表、字段集合中有属性表、方法集合中有属性表。

java虚拟机规定了有哪些属性,随着java的发展,规定的属性还在增加,虚拟机会忽略掉不认识的属性。下面只给了9个属性:


这些属性就不详解了,只有方法有的属性,在属性表中才会出现。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,647评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,690评论 2 374
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,739评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,692评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,552评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,410评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,819评论 3 387
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,463评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,752评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,789评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,572评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,414评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,833评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,054评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,345评论 1 254
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,810评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,016评论 2 337

推荐阅读更多精彩内容