在《【编译引擎】学习阅读Class文件结构(16进制版)-上》中,笔者讲解了如何以16进制的方式,解读Demo2.java对应字节码的常量池。
我们继续解读Demo2.class字节码的属性表、方法表等重要的片段。
1.对类的描述:访问标识
JVM规范:The value of the
access_flags
item is a mask of flags used to denote access permissions to and properties of this class or interface.
在字节码文件中,紧接着常量池之后,2个字节表示了当前类或者接口访问标识。
它的结构如下:
我们看一下第35个常量之后紧接着的2个字节:
这个值=00 21,对照JVM规范可知,0x0021 = 0x0001 | 0x0020,表示当前类是public类型的,如下图所示的含义:
这两个字节,表示的就是演练代码中红框处内容:
2.继承结构:类索引/父类索引/接口列表
JVM规范:
The value of the
this_class
item must be a valid index into theconstant_pool
table.For a class, the value of the
super_class
item either must be zero or must be a valid index into theconstant_pool
table.The value of the
interfaces_count
item gives the number of direct superinterfaces of this class or interface type.
2个字节表示当前类索引
2个字节表示父类索引
2个字节表示接口计数器、N个字节(N=接口计数器的值)表示接口索引列表(每个接口索引占2个字节)。
我们看一下下图红框处的6个字节:
00 06表示常量池中第6个常量(CONSTANT_Class类型),进而指向了第28个常量(CONSTANT_Utf8类型),表示的就是Demo2类。
00 07表示常量池中第6个常量(CONSTANT_Class类型),进而指向了第28个常量(CONSTANT_Utf8类型),表示的就是Demo2的父类——Object类。
00 00表示接口计数器为0,表示Demo2类没有实现任何接口。
这些字节码表达了红框处代码的信息:
3.类的成员们:字段表
3.1.字段计数器
JVM规范:The value of the
fields_count
item gives the number offield_info
structures in thefields
table
红框处的2个字节00 01,表示Demo2类中有1个字段
3.2.JVM如何表示一个字段
JVM规范:Each field is described by a
field_info
structure.No two fields in one
class
file may have the same name and descriptor
JVM为了表示类中的一个字段(例如:一个实例变量),用如下数据结构表示:
访问标志:2个字节,就是这个字段的访问权限,例如:private、public等。
字段名索引:2个字节,就是这个字段的在常量池中的索引。
字段描述符:2个字节,就是这个字段的类型。
3.3.字段访问标志
JVM规范:The value of the
access_flags
item is a mask of flags used to denote access permission to and properties of this field.
JVM认为字段访问标志包括:public、private、protected,也包括static、final、volatile、transient,还包括enum修饰符
具体如下表:
在Demo2类的字节码中,显示值为00 02,说明Demo2有1个字段,字段为private。
对应Demo2的代码如下图红框:
3.3.字段名索引
JVM规范:The value of the
name_index
item must be a valid index into theconstant_pool
table.
如同其它"索引"类型的信息片段,JVM也是用在常量池的索引表示字段名。
在Demo2的字节码中,字段名索引值=00 08
00 08表示了常量池第8个常量,第8个常量为CONSTANT_Utf8类型,就是字面量"field1”
Demo2源码中,也说明,Demo2类中实例变量名就是field1
3.4.字段描述符
JVM规范:The value of the
descriptor_index
item must be a valid index into theconstant_pool
table.
2个字节的字段描述符,就是表示字段的数据类型,例如:int、byte、char、double或者对象引用等。
Demo2的字节码中,00 09就是字段field1的描述符:
00 09表示field1的类型可以去查常量池的第9个常量,第9个常量是CONSTANT_Utf8类型,值为I,I表示的就是int
看到Demo2的源码中,field1的数据类型就是int:
4.类的行为集合:方法表
4.1.方法计数器
JVM规范:The value of the
methods_count
item gives the number ofmethod_info
structures in themethods
table.
JVM用2个字节,表示类中具有多少个方法。
Demo2的字节码中,00 02说明有2个方法。
可是Demo2的源码中,不是只有一个方法hello吗?
那是因为Demo2还有一个构造函数,JVM将构造函数命名为
JVM还会在某些场景下生成
4.2.JVM如何表示一个方法
JVM规范:Each method, including each instance initialization method and the class or interface initialization method, is described by a
method_info
structure.
要用一种数据结构表达方法,最基本的有3个要素:
- 方法的访问权限
- 方法名
- 方法返回值类型
进一步,方法体、方法内的局部变量等等怎么表达呢?
JVM采用了属性表来表示它们,这一部分,也是笔者认为"广义的编译器"非常聪明、非常巧妙的信息表示方法。
下图摘自JVM规范,方法内部的信息采用属性表表示,属性表中每一个属性又可以嵌套一个子属性表。
这样就形成了一个多级的属性树。
这里表述的很抽象,我们接下来以Demo2中的hello方法为例,具象化地理解JVM表达方法的method_info以及下属的属性树。
下图红框,就是hello方法,在字节码中的信息片段。
4.3.hello方法的访问标识符
JVM规范:The value of the
access_flags
item is a mask of flags used to denote access permission to and properties of this method.
方法访问标识符包括:public/private/protected,也包括static/final,还包括synchronized/native,以及abstract等等。
可以看到方法的访问标识符就是Java代码中,在方法名前面,对方法的各种访问限定,如下图:
Demo2的字节码中,访问标识符=00 01,根据上表,说明hello方法是public类型的:
对照Demo2的源代码,如下图:
4.4.hello方法名
JVM规范:The value of the
name_index
item must be a valid index into theconstant_pool
table.
这2个字节也是索引类型,指向了常量池中的常量。
Demo2的字节码中,00 11指向了第17个常量。
根据常量池中的第17个常量,进而可以得到方法名为"hello”,如下图:
对照Demo2的源码,如下图:
4.5.hello方法的描述符
JVM规范:The value of the
descriptor_index
item must be a valid index into theconstant_pool
table.
方法描述符,可以理解为方法的输入参数类型列表和返回值类型
Demo2字节码的方法描述为00 08,指向了常量池中第11个常量,第11个常量的值=**"()V”**。
()表示没有输入参数,V表示void类型的返回值:
对应Demo2的源代码:
4.6.hello方法的属性计数器
JVM规范:The value of the
attributes_count
item indicates the number of additional attributes of this method.
方法下,下挂了属性树,这2个字节就表示了属性个数。
Demo2的字节码中,00 01表示hello方法下挂了1个属性
在此,我们先剧透一下hello方法下挂的属性树结构:
从上图,我们可以得到2个信息:
- JVM对于类中的方法,都会下挂一个Code属性,Code属性像一个容器,下面可以挂更多的子属性
- LineNumberTable属性、LocalVariableTable属性,都是一种表结构类型的属性
5.总结:JVM如何描述一个类?
根据Demo2演练代码,JVM描述了如下信息:
5.1.类的信息
JVM字节码中构建了这样一颗与类有关的信息树
有一个叫Demo2的类,是public类型的,继承于Object类
5.2.类中字段相关的信息
JVM构建了字节相关的信息树,遍历可知:
Demo2类中有一个字段field1,是private的,是int类型的
5.3.类中方法相关的信息
JVM构建了方法相关的信息树,遍历可知:
Demo2类中有一个方法叫做hello,是public的,没有输入也没有返回
6.下一步
笔者下篇会继续阅读本演练代码对应的Class文件剩余部分,包括
方法内部相关的信息
如果您能耐心地看到这句话,恭喜您,距离”看着16进制享受而诡魅的微笑“又近了一步,加油!