在《【编译引擎】学习阅读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个字节表示了当前类或者接口访问标识。

它的结构如下:

image-20201210103634981

我们看一下第35个常量之后紧接着的2个字节:

image-20201210104039071

这个值=00 21,对照JVM规范可知,0x0021 = 0x0001 | 0x0020,表示当前类是public类型的,如下图所示的含义:

image-20201210104405014

这两个字节,表示的就是演练代码中红框处内容:

image-20201210105237723

2.继承结构:类索引/父类索引/接口列表

JVM规范:

The value of the this_class item must be a valid index into the constant_pool table.

For a class, the value of the super_class item either must be zero or must be a valid index into the constant_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个字节)。

image-20201210105451548

我们看一下下图红框处的6个字节:

image-20201210110411942

00 06表示常量池中第6个常量(CONSTANT_Class类型),进而指向了第28个常量(CONSTANT_Utf8类型),表示的就是Demo2类。

00 07表示常量池中第6个常量(CONSTANT_Class类型),进而指向了第28个常量(CONSTANT_Utf8类型),表示的就是Demo2的父类——Object类。

00 00表示接口计数器为0,表示Demo2类没有实现任何接口。

image-20201210142241019

这些字节码表达了红框处代码的信息:

image-20201210143458209

3.类的成员们:字段表

3.1.字段计数器

JVM规范:The value of the fields_count item gives the number of field_info structures in the fields table

image-20201210143849778

红框处的2个字节00 01,表示Demo2类中有1个字段

image-20201210144133507

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个字节,就是这个字段的类型。

image-20201210145124123

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修饰符

具体如下表:

image-20201210144454329

在Demo2类的字节码中,显示值为00 02,说明Demo2有1个字段,字段为private。

image-20201210144522844

对应Demo2的代码如下图红框:

image-20201210145650738

3.3.字段名索引

JVM规范:The value of the name_index item must be a valid index into the constant_pool table.

如同其它"索引"类型的信息片段,JVM也是用在常量池的索引表示字段名。

image-20201210150016307

在Demo2的字节码中,字段名索引值=00 08

image-20201210150106811

00 08表示了常量池第8个常量,第8个常量为CONSTANT_Utf8类型,就是字面量"field1”

image-20201210150325539

Demo2源码中,也说明,Demo2类中实例变量名就是field1

image-20201210150529362

3.4.字段描述符

JVM规范:The value of the descriptor_index item must be a valid index into the constant_pool table.

2个字节的字段描述符,就是表示字段的数据类型,例如:int、byte、char、double或者对象引用等。

image-20201210150830424

Demo2的字节码中,00 09就是字段field1的描述符:

image-20201210151009853

00 09表示field1的类型可以去查常量池的第9个常量,第9个常量是CONSTANT_Utf8类型,值为I,I表示的就是int

image-20201210151218499

看到Demo2的源码中,field1的数据类型就是int

image-20201210151241013

4.类的行为集合:方法表

4.1.方法计数器

JVM规范:The value of the methods_count item gives the number of method_info structures in the methods table.

JVM用2个字节,表示类中具有多少个方法。

image-20201210152620970

Demo2的字节码中,00 02说明有2个方法。

image-20201210152702983

可是Demo2的源码中,不是只有一个方法hello吗?

那是因为Demo2还有一个构造函数,JVM将构造函数命名为方法。

JVM还会在某些场景下生成方法,笔者将在后续文章中阐述。

image-20201210152848314

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规范,方法内部的信息采用属性表表示,属性表中每一个属性又可以嵌套一个子属性表。

这样就形成了一个多级的属性树。

image-20201210155258368

这里表述的很抽象,我们接下来以Demo2中的hello方法为例,具象化地理解JVM表达方法的method_info以及下属的属性树。

下图红框,就是hello方法,在字节码中的信息片段。

image-20201210152525140

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代码中,在方法名前面,对方法的各种访问限定,如下图:

image-20201210163636030

Demo2的字节码中,访问标识符=00 01,根据上表,说明hello方法是public类型的:

image-20201210163418660

对照Demo2的源代码,如下图:

image-20201210163938998

4.4.hello方法名

JVM规范:The value of the name_index item must be a valid index into the constant_pool table.

这2个字节也是索引类型,指向了常量池中的常量。

image-20201210164225563

Demo2的字节码中,00 11指向了第17个常量

image-20201210164206078

根据常量池中的第17个常量,进而可以得到方法名为"hello”,如下图:

image-20201210164338588

对照Demo2的源码,如下图:

image-20201210164353158

4.5.hello方法的描述符

JVM规范:The value of the descriptor_index item must be a valid index into the constant_pool table.

方法描述符,可以理解为方法的输入参数类型列表和返回值类型

image-20201210164948299

Demo2字节码的方法描述为00 08,指向了常量池中第11个常量,第11个常量的值=**"()V”**。

image-20201210164907934

()表示没有输入参数,V表示void类型的返回值:

image-20201210165507268

对应Demo2的源代码:

image-20201210165604070

4.6.hello方法的属性计数器

JVM规范:The value of the attributes_count item indicates the number of additional attributes of this method.

方法下,下挂了属性树,这2个字节就表示了属性个数。

image-20201210171021692

Demo2的字节码中,00 01表示hello方法下挂了1个属性

image-20201210170911839

在此,我们先剧透一下hello方法下挂的属性树结构:

image-20201210170336772

从上图,我们可以得到2个信息:

  • JVM对于类中的方法,都会下挂一个Code属性,Code属性像一个容器,下面可以挂更多的子属性
  • LineNumberTable属性、LocalVariableTable属性,都是一种表结构类型的属性

5.总结:JVM如何描述一个类?

根据Demo2演练代码,JVM描述了如下信息:

5.1.类的信息

JVM字节码中构建了这样一颗与类有关的信息树

有一个叫Demo2的类,是public类型的,继承于Object类

image-20201210174253160

5.2.类中字段相关的信息

JVM构建了字节相关的信息树,遍历可知:

Demo2类中有一个字段field1,是private的,是int类型的

image-20201210174530083

5.3.类中方法相关的信息

JVM构建了方法相关的信息树,遍历可知:

Demo2类中有一个方法叫做hello,是public的,没有输入也没有返回

image-20201210174755908

6.下一步

笔者下篇会继续阅读本演练代码对应的Class文件剩余部分,包括

方法内部相关的信息

如果您能耐心地看到这句话,恭喜您,距离”看着16进制享受而诡魅的微笑“又近了一步,加油!

image-20201210174921805