虚拟机类加载机制(二)--- 类文件结构之属性表详解

Java的技术体系包括

  • 支持Java程序运行的虚拟机(JVM)
  • 提供接口支持的Java API
  • Java 编程语言
  • 第三方Java框架(如Spring等)

代码编译的结果从本地机器码转变为字节码,是存储格式发展的一小步,却是编程语言的一大步。


上一篇文章我们详细介绍了由java类编译而成的Class文件的结构,其中最后一项属性表集合(attribute_info)内容较多,且我们使用Java编写的代码逻辑,大部分都在此表示,因此单独拿出一篇文章详细介绍属性表的内容。在第七版的虚拟机规范中已经预定了21项属性表,且还可以自定义属性表,因此属性表很多,我们详细了解一下其中最为常用的几项

Code属性

一个类中的方法中的代码,经过编译之后,最终变为字节码指令存储在Code属性内。Code属性位于方法表(method_info)的属性集合之中,但并非所有的方法都有这个属性,比如接口或者抽象类的方法就没有。Code属相的结构如下图所示

类型 名称 数量 描述
u2 attribute_name_index 1 属性名称
u4 attribute_length 1 属性总长度
u2 max_stack 1 操作数栈最大深度
u2 max_locals 1 局部变量表所需要的存储空间,编译器会根据局部变量的作用域等计算出该值的大小
u4 code_length 1 字节码长度
u1 code code_length 用来存储字节码指令的一系列字节流
u2 exception_table_length 1 异常表长度
exception_info exception_table exception_table_length 显示异常处理表
u2 attribute_count 1
attribute_info attributes attributes_count

其中code和code_length用来表示Java程序编译后生成的字节码指令。code_length代表字节码的长度,code用于存储字节码指令的一系列字节流。字节码指令,顾名思义,就是一个字节代表的指令,一个字节代表的值范围是0~255,也就是说总共可以表达256种指令,目前虚拟机规范定义了大约200条指令。code_length是一个u4类型的数据,理论上表达的意思是字节码指令的最大长度可以达到2的32次方-1,但虚拟机规范中明确规定一个方法不允许超过65535条字节码指令,也就是两个字节所能表示的最大值。

max_locals中的单位是Slot,Slot是虚拟机为局部变量分配内存所使用的最小单位,对于长度不超过32位的数据类型,每个局部变量表占用1个Slot,如byte, char, float, int, short, boolean, returnAdress等。对于double和long这两种64位数据类型需要两个Slot

Code属性是Class文件中最最重要的一个属性。因为一个Java类中表达的信息,我们可以将其分为两部分元数据(包括类、字段、方法定义和其他信息)和代码逻辑(方法中的代码),那么在整个Class文件中,Code 属性用来描述代码逻辑,其他的数据项均是来描述元数据的。换句话讲,我们所写代码的主体逻辑,告诉计算机该做什么事情,都是在Code属性中包含。

异常表的格式如下所示,具体含义为,如果当字节码在第start_pc行到end_pc行之间出现了catch_type类型或者其子类的异常,则转到第handler_pc行继续执行。

类型 名称 数量 类型 名称 数量
u2 start_pc 1 u2 handler_pc 1
u2 end_pc 1 u2 catch_type 1

Exceptions 属性

这里的Exception属性是在方法表中与Code属性平级的一项属性,表示方法描述时,在throws关键字后面列举的异常。结构如下

类型 名称 数量
u2 attribute_name_index 1
u4 attribute_length 1
u2 number_of_exceptions 1
u2 exception_index_table number_of_exceptions

LineNumberTable属性

LineNumberTable属性用于描述java代码行号与字节码行号(字节码的偏移量)之间的对应关系。它不是运行时必须的属性。如果没有这项属性,不会对程序的运行产生任何影响,但是当程序抛出异常时,堆栈中没有出错的行号,而且在调试程序时,也无法按照源码行来调试断点。默认会在Class文件中生成这一项,可以使用如下命令取消生成

javac -g:none或者-g:lines

它的结构如下

类型 名称 数量
u2 attribute_name_index 1
u4 attribute_length 1
u2 line_number_table_length 1
line_number_info line_number_table line_number_table_length

其中line_number_info表包含了start_pc和line_number两个u2类型的数据,前者是字节码行号,后者是源码行号。

LocalVariableTable属性

LocalVariableTable属性用于描述栈帧中局部变量表中的变量和java源码中的变量之间的关系。它也不是运行时必须的属性,但默认会生成到Class文件中。如果没有这项属性,当别人调用这个方法的时候,所有参数名称都会丢失,被arg0,arg1之类的占位符代替,会对代码编写带来不方便。可以使用如下命令取消生成

javac -g:none或者-g:vars

它的结构如下

类型 名称 数量
u2 attribute_name_index 1
u4 attribute_length 1
u2 local_variable_table_length 1
local_variable_info lacal_variable_table local_variable_table_length

其中local_variable_info表示一个栈帧与源码中的局部变量的对应关系,结构和含义如下所示

类型 名称 数量 描述
u2 start_pc 1 局部变量生命周期开始的字节码偏移量
u2 length 1 作用范围的长度
u2 name_index 1 局部变量名称,指向常量池的索引
u2 descriptor_index 1 局部变量描述符,指向常量池的索引
u2 index 1 局部变量在栈帧中局部变量表中Slot的位置

SourceFile属性

SourceFile属性用于记录生成这个Class文件的源码文件名称。这个属性也是可选的,如果不生成这项属性,当抛出异常时,堆栈中将不会显示出错代码所属的文件名。可使用如下命令取消生成这项属性

javac -g:none或者-g:source

这个属性是一个定长的属性,它的结构如下

类型 名称 数量 描述
u2 attribute_name_index 1
u4 attribute_length 1
u2 source_index 指向常量池中CONSTANT_Utf8_info类型常量的索引,常量值是源码文件的文件名

ConstantValue属性

ConstantValue属性的作用是通知虚拟机自动为静态变量赋值。只有被static关键字修饰的变量才可以使用这项属性。对于静态变量,有两种时刻可以赋值,一种是在类构造器<clinit>,另一种是使用ConstantValue属性。各种编译器的实现可能不同。我们常用的Sun javac编译器的选择是,对于同时使用final和static的常量,并且它的类型是基本类型或者String类型,就生成ConstantValue来赋值。如果没有被final修饰,或者非基本类型或者字符串,则会选择在<clinit>中进行初始化。从虚拟机规范的角度来讲,ConstantValue属性并没有要求必须设置了ACC_FINAL标志,而是要求了必须设置有ACC_STATIC标志。

该属性的结构如下,它也是一个定长的属性

类型 名称 数量 描述
u2 attribute_name_index 1
u4 attribute_length 1
u2 constantcalue_index 指向常量池中的索引

InnerClass属性

InnerClass属性用于记录内部类与宿主之间的关联。如果一个类中定义了内部类,编译器就会分别在这个类和它的内部类中生成该属性。该属性的结构如下

类型 名称 数量 描述
u2 attribute_name_index 1
u4 attribute_length 1
u2 number_of_classes 1 记录有多少个内部类
inner_classes_info inner_classes 1 每个内部类是一个该类型的表

inner_classes_info的结构如下

类型 名称 数量 描述
u2 inner_class_info_index 1 指向常量池中CONSTANT_Class_info型常量,代表内部类的符号引用
u2 outer_class_info_index 1 指向常量池中CONSTANT_Class_info型常量,代表外部类的符号引用
u2 inner_name_index 1 指向常量池中CONSTANT_Utf8_info型常量,代表内部类的名称
u2 inner_class_access_flags 1 代表内部类的访问标志,类似于access_flags

其中inner_class_access_flags标志位的含义如下表

标志名称 标志值 含义
ACC_PUBLIC 0x0001 内部类是否为public
ACC_PRIVATE 0x0002 内部类是否为private
ACC_PROTECTED 0x0004 内部类是否为protected
ACC_STATIC 0x0008 内部类是否为static
ACC_FINAL 0x0010 内部类是否为final
ACC_SYNCHRONIZED 0x0020 内部类是否为synchronized
ACC_ABSTRACT 0x0400 内部类是否为abstract
ACC_SYNTHETIC 0x1000 内部类是否由编译器自动产生
ACC_ANNOTATION 0x2000 内部类是一个注解
ACC_ENUM 0x4000 内部类是一个枚举

Deprecated 与 Synthetic 属性

这两个属性都属于标志类型的布尔属性,要么有要么没有,不存在属性值的概念。其中,Deprecated用于表示某个类或者方法字段已经被认定为不推荐使用,它可以通过在代码中使用 @deprecated 注释进行设置。Synthetic属性代表某个字段或者方法不是由java源代码产生,而是由编译器自动添加的。这两个属性的结构如下,其中attribute_length的值必须为0。

类型 名称 数量
u2 attribute_name_index 1
u4 attribute_length 1

编译器自动生成的实例构造器<init>和类构造器<clinit>除外

StackMapTable属性

该属性是JDK1.6之后增加到Class文件规范中的,它是一个复杂的变长属性,位于Code属性表中。它的功能用于,在类加载的字节码验证阶段用于验证字节码逻辑合法性,它的工作原理在虚拟机规范中用了整整120页来讲解,因此十分复杂。我们只需要知道该属性的含义就够了。它的结构如下

类型 名称 数量
u2 attribute_name_index 1
u4 attribute_length 1
u2 number_of_entries 1
stack_map_frame stack_map_frame_entries number_of_entries

虚拟机规范中规定,在Class版本号大于50的文件中,如果方法的Code属性中没有StackMapTable属性,则表示它带有一个隐式的StackMap属性。这个StackMapTable属性的作用等同于number_of_entries值为0的属性。一个方法的Code属性最多只能有一个StackMapTable属性,否则会抛出ClassFormatError异常。

Signature属性

Signature属性在JDK1.5之后增加到了Class文件中,它是一个可选的定长属性,可以出现在类、字段、方法的属性表中。它的作用是,任何类、接口、初始化成员或者方法的泛型签名如果包含了类型变量或参数化类型,则Signature会为它记录泛型签名信息。它的在结构如下

类型 名称 数量
u2 attribute_name_index 1
u4 attribute_length 1
u2 signature_index 1

其中signature_index项的值是一个对常量池的索引。常量池在该索引处的项是一个CONSTANT_Utf8_info结构,表示类签名,方法类型签名或者字段签名。

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

推荐阅读更多精彩内容