字节码文件格式(深入Jvm读书笔记一)

Class类文件的结构

Class文件是一组以8位为基础单位的二进制流,当需要暂用8位字节以上空间的数据时,则会按照高位在前的方式分割成若干个8位字节进行存储。
根据Java虚拟机规范的规定,Class文件格式采用一种类似于C语言结构体的伪结构来存储。包含两种数据类型:无符号数和表。其中无符号数属于基本的数据类型,以u1 u2 u4 u8 分别代表1个字节、2个字节、4个字节和8个字节的无符号数,无符号数可以用来描述数字、索引引用,数量值,或者按照啊UTF-8编码构成字符串。表是由多个无符号数或其他表作为数据项构成的复合数据类型,表习惯性以_info结尾。无论是无符号数还是表,当需要描述同一类型但数量不定的多个数据时,经常会使用一个前置的容量计数器加若干连续的数据项的形式。

魔数与Class文件的版本

Class文件的前4个字节为魔数,为0xCAFEBABE,紧接着4个字节存储的是Class文件的版本号,第五第六个字节是次版本号,第七第八个是主版本号。高版本的JDK能向下兼容以前版本的Class文件,但不能裕兴以后版本的Class文件,即使文件格式未发生变化。

常量池

紧接着是常量池入口。常量是Class文件空间最大的数据项目之一。常量池容量为u2数据类型,其索引值是从1开始的,例如0x0016为十进制22,但实际只有21(1~21)个常量。常量池中索引为0代表不引用任何常量。Class文件中只有常量池索引是从1开始的,其他的都与一般习惯相同,从0开始。
常量池主要存放两类常量:字面常量和符号引用。字面常量如文本字符串、被声明为final的常量值。而符号引用常量包括下面三类常量:

  • 类和接口的全限定名
  • 字段的名称和描述符(Descriptor)
  • 方法的名称和描述符
    Class文件加载的过程中虚拟机需要根据索引去常量池解析对应的值。
    常量池中每一项常量都是一个表,共有11中各不相同的表结构数据,这些表结构都有一个特点就是第一位是一个u1类型的标志位,代表常量类型。


CONSTANT_Class_info的结构主要包括 u1(表示常量类型) u2(name_index) name_index是一个索引值,它指向常量池中一个CONSTANT_Utf8_info类型的常量,此常量代表了这个类或者接口的全限定名。
CONSTANT_Utf8_info的结构为u1(tag) u2(length) u1(bytes)(length 个), 其中length字段说明这个utf-8编码的字符串的长度是多少字节,它后面紧跟着的长度为length字节的连续数据。utf-8编码中从'\u0001'到'\u007f'之间的字符的缩略吗使用一个字节表示,从'\u0080'到'\u07ff'之间的所有字符的缩略编码用两个字节表示。从'\u0800'到'\uffff'之间的所有字符缩略码使用三个字节表示。我们使用javap -verbose 可以将字节码内容打印出来。
常量池中数据类型的结构如下:


常量池后面是u2代表访问标志(access_flags),这个标志用于标识当前class的一些类或者接口层次的访问信息。具体如下:


this_class 和 super_class都是一个u2索引类型,用于标识类和父类的全限定名。interfaces用于标识接口集合。

类中字段

field_info 用于描述接口或类中声明的变量。包括静态和非静态的变量。字段表的结构如下:


其中name_index描述字段名称 descriptor描述的是字段的描述符(这个类似jni中的描述类似)attribute_info是字段的额外描述信息。
filed_info中不会列出继承来的字段,但有可能会列出原本java代码中不存在的字段,譬如内部类会添加指向外部类的字段。

方法表

方法表的内容和字段表基本是一致的,依次包括 access_flags name_index decriptor_index attributes.
方法里的java代码,经过编译成字节码指令后,存放在方法属性表集合中一个名为code的属性里面。在java语言中只靠返回值类型不同不能构成方法的重载。而在class文件中是可以的。

属性表(attribute_info)集合

与class文件中其他的数据项要求严格的顺序、长度和内容不同,属性表并不要求各个属性有严格的顺序,并且只要不与已知的属性名重复就可以。任何人实现的编译器都可以向属性表中写入自己定义的属性信息,Java虚拟机运行时会忽略掉它不认识的属性。为了能正确地解析class文件,虚拟机应当识别的属性如下:


属性表中至少定义的字段

对于每个属性它的名称它的名称为u2的索引类型,而属性值的结构则是完全自定义的,只需要声明属性值所占用的位长度即可。其主要字段包括:u2(attribute_name_index) u2(attribute_length) u1(info)

Code属性

并非所有的方法都必须有这个属性,例如接口或抽象类中的方法就不存在Code属性。Code属性的结构如下:

Code的属性

attribute_length 指示了属性值的长度,由于属性名称索引和属性长度移动是6个字节,所以属性值的长度固定为整个属性表的长度减去6个字节。
max_stack代表了操作数栈深度的最大值。在执行任意指令的时候操作数栈都不会超过这个深度。虚拟机运行的时候需要根据这个值来分配栈中操作栈的深度。
max_locals代表了局部变量表所需要的存储空间。在这里max_locals单位是slot,slot是虚拟机为局部变量分配内存所使用的最小单位。对于byte,char,float,int,short,bootlean,reference,returnAddress等长度不超过32位的数据类型,每个局部变量占用1个slot,而double和long这两种64位的数据类型则需要2个slot.方法参数、显式异常处理器的参数、方法体中定义的局部变量需要使用局部变量表来存放。但是并不是方法中使用的局部变量的总数作为max_locals,因为局部变量表中的slot可以被重复使用。编译器会根据变量的作用域来分类slot并分配给各个变量使用,然后计算出max_locals的大小。
code_length 和 code用来存储字节码指令。code中每一条指令都是u1类型,一共可以表达256条指令,目前java虚拟机规范已经定义了约200条指令的含义。由于code_length 是u4类型但是虚拟机限制一个方法中不允许超过65535条指令,如果超过java编译器会拒绝编译。
在字节码指令之后的是这个方法的显式的异常处理表集合,异常表包含四个字段:如果字节码从第start_pc行到第end_pc行之间出现了类型为catch_type或其子类的异常(catch_type为指向一个CONSTANT_Class_info型常量的索引)则转到handler_pc行继续处理。当catch_type为0是代表任何异常情况都要转向handler_pc处进行处理。异常表格式如下:
异常表

异常表实际上是Java代码的一部分,编译器使用异常表而不是简单的跳转命令来实现Java异常及finallly处理机制。

Exceptions属性

Exceptions 属性作用是列举出方法中可能抛出的受检查异常。其结构表如下:



此属性中的number_of_exceptions项表示方法可能抛出number_of_exceptions中受检异常,每一种受检查的异常使用一个exception_index_table项表示。

LineNumberTable属性

主要用于描述Java源代码行号与字节码行号之间的对应关系。它并不是运行时必须的属性,但默认会生成到Class文件之中,可以在Javac中使用-g:none 或-g:lines选项来取消或要求生成这项信息。其格式如下:



line_number_table是一个数量为line_number_table_length 类型为line_number_info的集合,line_number_info表包括了start_pc和line_number两个u2类型的数据项,前者是字节码行号,后者是Java源码行号。

LocalVariableTable属性

用于描述栈帧中局部变量表中的变量与Java源码中定义的变量之间的关系,他们不是运行时必须的属性,默认也不会生成到Class文件中。如果没有生成这个属性,最大的影响是当其他引用这个方法时,所有的参数名称都将丢失,IDE可能会使用arg0、arg1等来代替原来的参数名。
其属性表如下:


LocalVariableTable

local_variable_info的结构如下所示:


local_variable_info

start_pc和length属性分别代表了这个局部变量的生命周期开始的字节码偏移量及其作用范围覆盖的长度。name_index和descriptor_index都是指向常量池中CONSTANT_Utf8_info型常量的索引,分别代表了局部变量的名称及该局部变量的描述符。
index是这个局部变量在栈帧局部变量中slot的位置,当这个变量的数据类型是64位类型时,它占用的slot为index和index+1两个位置。

SourceFile属性

用于记录生成这个Class文件的源码文件名称。这个属性也是可选的。对于大多数类来说,类名和文件名是一致的,但是内部类除外。如果没有这个属性,当抛出异常的时候,堆栈中不会显示出错误代码所属的文件名,其结构如下:


SourceFile

ConstantValue属性

ConstantValue属性的作用是通知虚拟机自动为静态变量赋值。只有被static关键字修饰的变量才可以使用这项属性。对于非static类型的变量的赋值时在实例构造器<init>方法中进行的,而对于类变量,有两种方式可选择,赋值在类的构造器<clinit>方法中进行,或者使用ConstantValue属性来赋值。目前Sun javac编译器选择是:如果同时使用static和final来修饰一个变量,并且这个变量的数据类型是基本类型或者是String类型的话,就生成ConstantValue属性来进行初始化,如果这个变量并没有被final修饰,或者是非基本数据类型及字符串类型,则选择在<clinit>方法中进行初始化。
虽然有final关键字才更符合ConstantValue的语义,但虚拟机规范中并没有请只要求字段必须设置ACC_FINAL标志,只要求ConstantValue属性的字段必须设置ACC_STATIC标志,对final关键字的要求是javac编译器自己加入的限制。ConstantValue属性的结构如下:


ConstantValue

InnerClasse

记录内部类与宿主类之间的关联。其属性表结构如下:


InnerClass

其中inner_classes_info表的结构如下:


inner_classes_info

Deprecated及Synthetic属性

这两个属性都是布尔型属性,Deprecated一般对应于代码中@deprecated注释。Synthetic属性代表此字段或方法并不是由Java源码直接产生的,而是由编译器自行添加的。自Java1.5以后可以设置访问标志中的ACC_SYNTHETIC标志位,所有非用户代码生成的类,方法及字段都应当至少设置Synthetic属性和ACC_SYNTHETIC标志位中的一个。唯一的例外是<init>和<clinit>

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

推荐阅读更多精彩内容

  • 我们知道Java最有名的宣传口号就是:“一次编写,到处运行(Write Once,Run Anywhere)”,而...
    卡巴拉的树阅读 1,464评论 0 6
  • 感觉写在一篇里面的话,篇幅会很长,所以拆成系列来写,每一篇专注一个主题,这样条理也会清晰一些。 首先,是完成这个系...
    Rangethan阅读 576评论 0 0
  • 前述 “相传昔有王氏子与陶氏女相好,父母夺其志,月夜同投此桥下,故名。” ——清.《...
    蓉团子阅读 494评论 0 1
  • 雨,整整下了一夜,注定这是一个不安分的夜。滴滴答答的声音骚扰了我即将进入梦乡,想要与周公来一场棋艺上的交流,但却被...
    吃昙花长大的小子阅读 225评论 0 1
  • 文|陆小墨 窗台、远景。 稀稀疏疏的人群,穿梭在时光里。这一刻进入的人,下一刻就出去了。 食堂,教室,宿舍。徘徊,...
    陆小墨阅读 467评论 0 2