ASM:(3)ClassFile
原文:https://lsieun.github.io/java-asm-01/asm-vs-classfile.html
ASM与ClassFile
我们都知道,在.class
文件中,存储的是ByteCode数据。但是,这些ByteCode数据并不是杂乱无章的,而是遵循一定的数据结构。
这个.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之间是什么样的关系呢?我们可以用下图来表示:
对于上图,我们用三句来描述它们的关系:
- 中间层-多个
.class
文件,虽然每个类里面的内容各不相同,但它们里面的内容都称为字节码(ByteCode)。 - 中下层-不论这些
.class
文件内容有怎样大的差异,它们都共同遵守同一个数据结构,即ClassFile。 - 中上层-为了方便于人们对于字节码(ByteCode)内容的操作,逐渐衍生出了各种操作字节码的类库。
- 上下层-不考虑中间层,我们可以说,不同的字节码类库是在同一个ClassFile结构上发展起来的。
既然有多个可以选择的字节码类库,那么我们为什么要选择ASM呢?这就得看ASM自身所具有的特点,或者说与众不同的地方。
ASM的特点
- 问题:与其它的操作Java字节码的类库相比,ASM有哪些与众不同的地方呢?
- 回答:在实现相同的功能前提下,使用ASM,运行速度更快(运行时间短,属于“时间维度”),占用的内存空间更小(内存空间,属于“空间维度”)。
ASM与ClassFile的关系
为了大家更直观的理解ASM与ClassFile之间关系,我们用下图来表示。其中,Java ClassFile相当于“树根”部分,ObjectWeb ASM相当于“树干”部分,而ASM的各种应用场景属于“树枝”或“树叶”部分。
学习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_info
、field_info
、method_info
和attribute_info
表示较为复杂的结构,但它们也是由u1
、u2
、u4
和u8
组成的。
相应的,在.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];
}