ASM:(3)ClassFile


原文:https://lsieun.github.io/java-asm-01/asm-vs-classfile.html

ASM与ClassFile

我们都知道,在.class文件中,存储的是ByteCode数据。但是,这些ByteCode数据并不是杂乱无章的,而是遵循一定的数据结构。

image-20220407151807993

这个.class文件遵循的数据结构就是由Java Virtual Machine Specification中定义的 The class File Format,如下所示。

ClassFile {
    u4             magic;
    u2             minor_version;
    u2             major_version;
    u2             constant_pool_count;
    cp_info        constant_pool[constant_pool_count-1];
    u2             access_flags;
    u2             this_class;
    u2             super_class;
    u2             interfaces_count;
    u2             interfaces[interfaces_count];
    u2             fields_count;
    field_info     fields[fields_count];
    u2             methods_count;
    method_info    methods[methods_count];
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}

字节码类库

ASM是操作字节码的类库,但并不是唯一的,还有许多其它的操作字节码的类库。

在下面列举了几个比较常见的字节码类库(按时间先后顺序):

  • Apache Commons BCEL:其中BCEL为Byte Code Engineering Library首字母的缩写。
  • Javassist:Javassist表示Java programming assistant
  • ObjectWeb ASM:本课程的研究对象。
  • Byte Buddy:在ASM基础上实现的一个类库

那么,字节码的类库和ClassFile之间是什么样的关系呢?我们可以用下图来表示:

image-20220407152849697

对于上图,我们用三句来描述它们的关系:

  • 中间层-多个.class文件,虽然每个类里面的内容各不相同,但它们里面的内容都称为字节码(ByteCode)。
  • 中下层-不论这些.class文件内容有怎样大的差异,它们都共同遵守同一个数据结构,即ClassFile。
  • 中上层-为了方便于人们对于字节码(ByteCode)内容的操作,逐渐衍生出了各种操作字节码的类库。
  • 上下层-不考虑中间层,我们可以说,不同的字节码类库是在同一个ClassFile结构上发展起来的。

既然有多个可以选择的字节码类库,那么我们为什么要选择ASM呢?这就得看ASM自身所具有的特点,或者说与众不同的地方。

ASM的特点

  • 问题:与其它的操作Java字节码的类库相比,ASM有哪些与众不同的地方呢?
  • 回答:在实现相同的功能前提下,使用ASM,运行速度更快(运行时间短,属于“时间维度”),占用的内存空间更小(内存空间,属于“空间维度”)。

ASM与ClassFile的关系

为了大家更直观的理解ASM与ClassFile之间关系,我们用下图来表示。其中,Java ClassFile相当于“树根”部分,ObjectWeb ASM相当于“树干”部分,而ASM的各种应用场景属于“树枝”或“树叶”部分

image-20220407153353979

学习ASM有三个不同的层次:

  • 第一个层次,ASM的应用层面。也就是说,ASM能够做什么?对于一个.class文件来说,我们可以使用ASM进行analysis、generation和transformation操作。
  • 第二个层次,ASM的源码层面。也就是,ASM的两个组成部分,它为分Core API和Tree API的内容。
  • 第三个层次,Java ClassFile层面。从JVM规范的角度,来理解.class文件的结构,来理解ASM中方法和参数的含义。

ClassFile快速参考

对于一个具体的.class而言,它是遵循ClassFile结构的。这个数据结构位于Java Virtual Machine Specification的 The class File Format部分。

ClassFile {
    u4             magic;
    u2             minor_version;
    u2             major_version;
    u2             constant_pool_count;
    cp_info        constant_pool[constant_pool_count-1];
    u2             access_flags;
    u2             this_class;
    u2             super_class;
    u2             interfaces_count;
    u2             interfaces[interfaces_count];
    u2             fields_count;
    field_info     fields[fields_count];
    u2             methods_count;
    method_info    methods[methods_count];
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}

其中,

  • u1: 表示占用1个字节
  • u2: 表示占用2个字节
  • u4: 表示占用4个字节
  • u8: 表示占用8个字节

cp_infofield_infomethod_infoattribute_info表示较为复杂的结构,但它们也是由u1u2u4u8组成的。

相应的,在.class文件当中,定义的字段,要遵循field_info的结构。

field_info {
    u2             access_flags;
    u2             name_index;
    u2             descriptor_index;
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}

同样的,在.class文件当中,定义的方法,要遵循method_info的结构。

method_info {
    u2             access_flags;
    u2             name_index;
    u2             descriptor_index;
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}

method_info结构中,方法当中方法体的代码,是存在于Code属性结构中,其结构如下:

Code_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 max_stack;
    u2 max_locals;
    u4 code_length;
    u1 code[code_length];
    u2 exception_table_length;
    {   u2 start_pc;
        u2 end_pc;
        u2 handler_pc;
        u2 catch_type;
    } exception_table[exception_table_length];
    u2 attributes_count;
    attribute_info attributes[attributes_count];
}
asm