字节码&ASM-class文件刨析

class文件基础

作为字节码于ASM专栏文章的开篇,一直在思考已何种风格去讲诉字节码和ASM,若想理解Java语言背后的技术字节码是无论无何都无法绕开的,而如果只是仅仅对字节码进行研究也是相当的枯燥,作为程序员的我们对这些我们不能动手写点啥子的相当难受,所以我想通过对字节码+操作字节码库的联动达到学以致用的目的

作为java的操刀手我们应该都知道在我们编写代码后java通过编译后的产物是class文件,而class文件也可以用类似于Class的结构描述文件。

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、u2、u4为1、2、4字节无符号整数,cp_info、field_info、method_info 、attribute_info 与之对应相同数据类型的可变长度集合(table),并且每个table都有着对应的长度字段constant_pool_count、interfaces_count、fields_count、methods_count、attributes_count。

magic

魔数,4字节,class文件头4字节,用于虚拟机验证class文件的合法性,如果我们修改class文件头4字节运行程序会抛出异常。Java使用0XCAFEBABE作为文件的标识。

minor_version

副版本号,2字节,该值为0

major_version

主版本号,2字节,java版本与之有着一一对应的值,比如java8→52。每当Java发布大版本时就增加1。虚拟机加载类文件会检查当前环境是否低于major_version,如果低于就会抛出异常

constant_pool_count & constant_pool[]

常量池数量,2字节,常量池,索引0为保留索引,Long和Double的常量需要暂用2个索引位,所以最多n - 1个

虚拟机目前共定义14中常量类型:

类型
CONSTANT_Utf8_info 1
CONSTANT_Integer_info 3
CONSTANT_Float_info 4
CONSTANT_Long_info 5
CONSTANT_Double_info 6
CONSTANT_Class_info 7
CONSTANT_String_info 8
CONSTANT_Fieldref_info 9
CONSTANT_Methodref_info 10
CONSTANT_InterfaceMethodref_info 11
CONSTANT_NameAndType_info 12
CONSTANT_MethodHandle_info 15
CONSTANT_MethodType_info 16
CONSTANT_InvokeDynamic_info 18

CONSTANT_Utf8_info

CONSTANT_Utf8_info存储经过MUTF-8后的字符串,tag标记类型,值1,1字节。length标记字符串长度,2字节。bytes真正存储字符串数据。由于length大小的限制,程序中生命的字面量字符串也存在这大小的限制。根据虚拟机规范我们可以得出理论上的字面量字符串长度最大位65535,但是经过实际测试字面量字符串的最大长度位65534,javac的作者如果不是出于特殊理由设置的机制那么这其实就可以算作javac的一个BUG了

CONSTANT_Utf8_info {
    u1 tag;
    u2 length;
    u1 bytes[length];
}
复制代码

CONSTANT_Integer_info

tag值3,bytes存储整形的值,4字节

CONSTANT_Integer_info {
    u1 tag;
    u4 bytes;
}
复制代码

CONSTANT_Float_info

tag值4,bytes存储浮点数的值,4字节

CONSTANT_Float_info {
    u1 tag;
    u4 bytes;
}
复制代码

CONSTANT_Long_info

tag值5,high_bytes存储Long高32位数据,low_bytes存储Long低32位数据

CONSTANT_Long_info {
    u1 tag;
    u4 high_bytes;
    u4 low_bytes;
}
复制代码

CONSTANT_Double_info

tag值6,high_bytes存储Double高32位数据,low_bytes存储Double低32位数据

CONSTANT_Double_info {
    u1 tag;
    u4 high_bytes;
    u4 low_bytes;
}
复制代码

CONSTANT_Class_info

tag值7,name_index指向常量池中类型CONSTANT_Utf8_info的索引,存储类全限定名

CONSTANT_Class_info {
    u1 tag;
    u2 name_index;
}
复制代码

CONSTANT_String_info

tag值8,string_index指向常量池中类型CONSTANT_Utf8_info索引,存储字符串真正的内容

CONSTANT_String_info {
    u1 tag;
    u2 string_index;
}
复制代码

CONSTANT_Fieldref_info

tag值9,class_index指向常量池中类型CONSTANT_Class_info索引,表示字段所属类,name_and_type_index指向常量池中类型CONSTANT_NameAndType_info索引,表示字段名字和类型

CONSTANT_Fieldref_info {
    u1 tag;
    u2 class_index;
    u2 name_and_type_index;
}
复制代码

CONSTANT_Methodref_info & CONSTANT_InterfaceMethodref_info

CONSTANT_Methodref_info tag值10,CONSTANT_InterfaceMethodref_info tag值11,二者区别在于是否接口方法,class_index指向常量池中类型CONSTANT_Class_info索引,表示方法所属类,name_and_type_index指向常量池中类型CONSTANT_NameAndType_info索引,表示方法名字和签名

CONSTANT_Methodref_info {
    u1 tag;
    u2 class_index;
    u2 name_and_type_index;
}
复制代码

CONSTANT_NameAndType_info

tag值12,name_index指向常量池中类型CONSTANT_Utf8_info索引,表示方法名、字段名,descriptor_index指向常量池中类型CONSTANT_Utf8_info索引,表示字段类型、方法签名

CONSTANT_NameAndType_info {
	u1 tag;
	u2 name_index;
	u2 descriptor_index;
}
复制代码

CONSTANT_MethodHandle_info

tag值15,该结构表示方法句柄,reference_kind值1~9,该值表示方法句柄的种类

CONSTANT_MethodHandle_info {
	u1 tag;
	u1 reference_kind;
	u2 reference_index;
}
复制代码

CONSTANT_MethodType_info

tag值16,表示一个方法类型

CONSTANT_MethodType_info {
	u1 tag;
	u2 descriptor_index;
}
复制代码

CONSTANT_InvokeDynamic_info

tag值18,用于实现lambda关键指令,bootstrap_method_attr_index指向bootstrap_method表索引,name_and_type_index表示由javac后通过ASM所生成对应的函数

CONSTANT_InvokeDynamic_info {
    u1 tag;
    u2 bootstrap_method_attr_index;
    u2 name_and_type_index;
}
复制代码

access_flags

访问标记位,2字节,标记类的修饰符,这些标记符可以相互组合出现,但是存在一定的互斥性,如果但从值考虑则有一条非常简单的规则就是同一位为非0值为互斥。这里多提一句的是在阅读源码不难发现相当多的库利用16进制来控制状态,得益于这样的设计,对状态的组合和校验都可以通过非常简单的算法实现,在个人项目中也可以尝试使用16进制实现某些适用的场景,体验16进制及位运算带来的简洁高效算法。

访问标记 JVM规范 含义
ACC_PUBLIC 0x0001 标记类公开权限是否public
ACC_FINAL 0x0010 标记类是否final类
ACC_SUPER 0x0020 已废除
ACC_INTERFACE 0x0200 标记是否接口
ACC_ABSTRACT 0x0400 标记是否抽象类
ACC_SYNTHETIC 0x1000 标记是否为生成的类
ACC_ANNOTATION 0x2000 标记是否注解类
ACC_ENUM 0x4000 标记是否枚举类

this_class

指向常量池中类型CONSTANT_Class_info索引,表示类全限定名,2字节

super_class

指向常量池中类型CONSTANT_Class_info索引,表示父类全限定名,2字节

interfaces_count & interfaces[]

接口表数量2字节,表中类型为CONSTANT_Class_info

{
		u2               interfaces_count;
		interface_info   interfaces[interfaces_count];
}
复制代码

fields_count & fields[]

字段表数量,2字节,表中类型为field_info,提供类或接口中字段的完整描述,表中仅包含类或接口声明的字段,不包含继承的父类或者实现的接口所包含的字段。

{
		u2               fields_count;
		field_info fields[fields_count];
}

field_info {
    u2             access_flags; 
    u2             name_index;
    u2             descriptor_index;
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}
复制代码

field_info:

  • access_flags
标记 含义
ACC_PUBLIC 0x0001 标记字段是否publick
ACC_PRIVATE 0x0002 标记字段是否private
ACC_PROTECTED 0x0004 标记字段是否protected
ACC_STATIC 0x0008 标记字段是否static
ACC_FINAL 0x0010 标记字段是否final
ACC_VOLATILE 0x0040 标记字段是否volatile
ACC_TRANSIENT 0x0080 标记字段是否transient,这个不经常使用, 标记后字段不能序列化
ACC_SYNTHETIC 0x1000 标记字段是否synthetic,这个由javac编译生成字段
ACC_ENUM 0x4000 标记字段是否为枚举
  • name_index

    指向常量池中CONSTANT_Utf8_info索引,表示字段名称

  • descriptor_index

    指向常量池中CONSTANT_Utf8_info索引,表示字段描述

  • attributes_count & attributes[]

    属性表,jvm规范约定字段属性结构为:Code_attribute、Exceptions_attribute、Synthetic_attribute、Signature_attribute、Deprecated_attribute、RuntimeVisibleAnnotations_attribute、RuntimeInvisibleAnnotations_attribute、RuntimeVisibleParameterAnnotations_attribute、RuntimeInvisibleParameterAnnotations_attribute、AnnotationDefault_attribute

methods_count & methods[]

方法表数量,2字节,表中类型为method_info,提供类或接口方法的完整描述,如果access_flags没有设置ACC_NATIVE|ACC_ABSTRACT则提供实现方法所对应的指令集,表中仅包含类或接口声明的方法,不包含继承的父类或者实现的接口所包含的方法。

{
		u2            methods_count;
		method_info   methods[methods_count];
}

method_info {
    u2             access_flags;
    u2             name_index;
    u2             descriptor_index;
    u2             attributes_count;
    attribute_info attributes[attributes_count];
}
复制代码

method_info:

  • access_flags
标记 含义
ACC_PUBLIC 0x0001 标记方法是否publick
ACC_PRIVATE 0x0002 标记方法是否private
ACC_PROTECTED 0x0004 标记方法是否protected
ACC_STATIC 0x0008 标记方法是否static
ACC_FINAL 0x0010 标记方法是否final
ACC_SYNCHRONIZED 0x0020 标记方法是否synchronized
ACC_BRIDGE 0x0040 javac生成的桥接方法
ACC_VARARGS 0x0080 标记方法是否含有可变参数
ACC_NATIVE 0x0100 标记方法是否native
ACC_ABSTRACT 0x0400 标记方法是否abstract
ACC_STRICT 0x0800 标记后方法浮点模式为FP-strict
ACC_SYNTHETIC 0x1000 javac生成方法标记
  • name_index

    指向常量池中CONSTANT_Utf8_info索引,表示特殊方法名称, |

  • descriptor_index

    指向常量池中CONSTANT_Utf8_info索引,表示方法签名,

  • attributes_count & attributes[]

    属性表,jvm规范约定方法属性结构为:Code_attribute、Exceptions_attribute、Synthetic_attribute、Signature_attribute、Deprecated_attribute、RuntimeVisibleAnnotations_attribute、RuntimeInvisibleAnnotations_attribute、RuntimeVisibleParameterAnnotations_attribute

attributes_count & attributes[]

属性表数量,2字节,属性是除了常量池之外最为复杂的结构,这里拿比较关心的几个进行介绍,其它可通过文档自行查阅

属性结构

Attribute
ConstantValue
Code
StackMapTable
Exceptions
InnerClasses
EnclosingMethod
Synthetic
Signature
SourceFile
SourceDebugExtension
LineNumberTable
LocalVariableTable
LocalVariableTypeTable
Deprecated
RuntimeVisibleAnnotations
RuntimeInvisibleAnnotations
RuntimeVisibleParameterAnnotations
RuntimeInvisibleParameterAnnotations
AnnotationDefault
BootstrapMethods

ConstantValue_attribute

常量字段值

ConstantValue_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 constantvalue_index;
}
复制代码

ConstantValue_attribute:

  • constantvalue_index

    指向常量池中CONSTANT_Long|CONSTANT_Float|CONSTANT_Double|CONSTANT_Integer|CONSTANT_String索引,存储字段初始化的值

Code_attribute

表示一个方法的指令集和一些额外的辅助信息

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];
}
复制代码

Code_attribute:

  • max_stack

    操作数栈最大深度,编译时确认,由指令的入栈和出栈计算出最大操作数栈长度最大值

  • max_locals

    本地变量表最大值,编译时确认,但并不是简单的直接计算整个帧栈中使用到的变量总和,而是通过对每个变量的生命周期的消亡而留给后面变量复用位减少了最终大小。

  • code_length & code[]

    存储方法指令集

  • exception_table_length & exception_table[]

    存储方法中相关的异常表

    • start_pc

      记录try索引,捕获异常开始

    • end_pc

      记录正常结束索引,若执行到这里表示代码try块已正常执行

    • handler_pc

      记录异常开始索引,若执行到这里表示代码try块内抛出catch_type类型异常代码走向catch代码块

    • catch_type

      记录异常类型,指向常量池中类型CONSTANT_Class_info索引

StackMapTable_attribute

由多个或零个帧栈组成,每个帧栈指定偏移量,该结构为了加快JVM运行速度而设计。由于设计过于复杂,在我们通过ASM编写代码时建议交权给ASM帮助我们处理

StackMapTable_attribute {
    u2              attribute_name_index;
    u4              attribute_length;
    u2              number_of_entries;
    stack_map_frame entries[number_of_entries];
}
复制代码

Exceptions_attribute

记录方法可能抛出哪些已处理异常

Exceptions_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 number_of_exceptions;
    u2 exception_index_table[number_of_exceptions];
}
复制代码
  • number_of_exceptions & exception_index_table[]

    异常表元素类型为常量池中类型为CONSTANT_Class_info,该类型必须为RuntimeException | Error子类**

InnerClasses_attribute

记录内部类和外部类关系

InnerClasses_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 number_of_classes;
    {   u2 inner_class_info_index;
        u2 outer_class_info_index;
        u2 inner_name_index;
        u2 inner_class_access_flags;
    } classes[number_of_classes];
}
复制代码
  • number_of_classes & classes[]
    • inner_class_info_index

      指向常量池类型为CONSTANT_Class_info索引,表示内部类

    • outer_class_info_index

      指向常量池类型为CONSTANT_Class_info索引,表示外部类

    • inner_name_index

      指向常量池类型为CONSTANT_Utf8_info索引,表示内部类全限定名

    • inner_class_access_flags

      内部类访问标记

LineNumberTable_attribute

标记字节码和源码对应行号

LineNumberTable_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 line_number_table_length;
    {   u2 start_pc;
        u2 line_number;	
    } line_number_table[line_number_table_length];
}
复制代码
  • line_number_table_length & line_number_table[]
    • start_pc

      字节码指令

    • line_number

      源码行号

LocalVariableTable_attribute

记录方法本地变量表

LocalVariableTable_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 local_variable_table_length;
    {   u2 start_pc;
        u2 length;
        u2 name_index;
        u2 descriptor_index;
        u2 index;
    } local_variable_table[local_variable_table_length];
}
复制代码
  • local_variable_table_length & local_variable_table[]
    • start_pc & length

      记录变量在字节码中存在的范围[start_pc, start_pc + length),也就是说这个时变量生命消亡记录的关键数据

    • name_index

      指向常量池中类型CONSTANT_Utf8_info索引,表示变量名称

    • descriptor_index

      指向常量池中类型CONSTANT_Utf8_info索引,表示变量类型签名

    • index

      表示变量位于帧栈本地变量表中位置,需要注意的时Long和Double占用两个位置

LocalVariableTypeTable_attribute

于LocalVariableTable_attribute记录信息类似,不同是它记录的不是描述而是签名

Deprecated_attribute

表示类、字段、方法已废除

RuntimeVisibleAnnotations_attribute

记录运行时注解信息

RuntimeVisibleAnnotations_attribute {
    u2         attribute_name_index;
    u4         attribute_length;
    u2         num_annotations;
    annotation annotations[num_annotations];
}

annotation {
    u2 type_index;
    u2 num_element_value_pairs;
    {   u2            element_name_index;
        element_value value;
    } element_value_pairs[num_element_value_pairs];
}

element_value {
    u1 tag;
    union {
        u2 const_value_index;

        {   u2 type_name_index;
            u2 const_name_index;
        } enum_const_value;

        u2 class_info_index;

        annotation annotation_value;

        {   u2            num_values;
            element_value values[num_values];
        } array_value;
    } value;
}
复制代码

annotation:

  • type_index

    指向常量池中类型CONSTANT_Utf8_info索引,表示注解描述类型

  • num_element_value_pairs & element_value_pairs[]

    • element_name_index

      指向常量池中类型CONSTANT_Utf8_info索引,表示注解字段描述

    • value

      类型element_value,描述注解字段对应值

RuntimeInvisibleAnnotations_attribute

于RuntimeVisibleAnnotations_attribute类似,但无法通过反射获取注解信息

RuntimeVisibleParameterAnnotations_attribute

于RuntimeVisibleAnnotations_attribute类似,但作用于方法参数

RuntimeInvisibleParameterAnnotations_attribute

于RuntimeInvisibleAnnotations_attribute类似,但作用于方法参数

AnnotationDefault_attribute

记录结构所表示元素的默认值

BootstrapMethods_attribute

虚拟机对动态语言lambda支持实现的重要结构,对invokedynamic指令的描述。

BootstrapMethods_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 num_bootstrap_methods;
    {   u2 bootstrap_method_ref;
        u2 num_bootstrap_arguments;
        u2 bootstrap_arguments[num_bootstrap_arguments];
    } bootstrap_methods[num_bootstrap_methods];
}
复制代码
  • num_bootstrap_methods & bootstrap_methods
    • bootstrap_method_ref

      指向常量池中类型为CONSTANT_MethodHandle_info索引,表示方法句柄

    • num_bootstrap_arguments & bootstrap_arguments[]

      元素指向常量池中CONSTANT_String_info、CONSTANT_Class_info、CONSTANT_Integer_info、CONSTANT_Long_info、CONSTANT_Float_info、CONSTANT_Double_info、CONSTANT_MethodHandle_info、CONSTANT_MethodType_info

掌握上诉构成class文件的结构也是字节码相关内容的起步,不积硅步无以至千里。

49580512-3C00-456d-8BD2-30E4F1A58076.png

图片是学习编程起步的hello world对应的class文件,文件使用16进制打开查看,这里提及下16进制的每个字符等于0.5字节,16进制中1个字符可以表示0~15,转换二进制 2 4 2^4 ,1字节等于8比特,所以推导出16进制的内存占用。

起始 结束 占用字节大小 描述
0,0 0,3 4 u2(CAFEBABE):魔数:0XCAFEBABE
0,4 0,5 2 u2(0000):副版本号:0
0,6 0,7 2 u2(0034)主版本号:0034 52 java8
0,8 0,9 2 u2(0022):常量池大小:0022 34 最大常量池数量为34 - 1 = 33
0,A 0,E 5 u1(0A):tag 10 CONSTANT_Methodref
u2(0006):所属类名索引6
u2(0014):方法名及签名索引20
0,F 1,3 5 u1(09):tag CONSTANT_Fieldref
u2(0015):所属类名索引21
u2(0016):字段名及类型索引22
1,4 1,6 3 u1(08):tag CONSTANT_String
u2(0017):字符串索引23
1,7 1,B 5 u1(0A):tag 10 CONSTANT_Methodref
u2(0018):所属类名索引24
u2(0019):方法名及签名索引25
1,C 1,E 3 u1(07):tag CONSTANT_Class
u2(001A):类全限定名索引26
1,F 2,1 3 u1(07):tag CONSTANT_Class
u2(001B):类全限定名索引27
2,2 2,A 9 u1(01):tag CONSTANT_Utf8
u2(0006):字符串长度6
内容:(3C 69 6E 69 74 3E)
2,B 3,0 6 u1(01):tag CONSTANT_Utf8
u2(0003):字符串长度3
内容:(28 29 56)()V
3,1 3,7 9 u1(01):tag CONSTANT_Utf8
u2(0004):字符串长度4
内容:(43 6F 64 65)Code
3,8 4,9 18 u1(01):tag CONSTANT_Utf8
u2(000F):字符串长度15
内容:(4C 69 6E 65 4E 75 6D 62 65 72 54 61 62 6C 65)LineNumberTable
4,A 5,E 21 u1(01):tag CONSTANT_Utf8
u2(0012):字符串长度18
内容:(4C 6F 63 61 6C 56 61 72 69 61 62 6C 65 54 61 62 6C 65)LocalVariableTable
5,F 6,5 7 u1(01):tag CONSTANT_Utf8
u2(0004):字符串长度4
内容:(74 68 69 73)this
6,6 7,4 15 u1(01):tag CONSTANT_Utf8
u2(000C):字符串长度12
内容:(4C 48 65 6C 6C 6F 57 6F 72 6C 64 3B)LHelloWorld;
7,5 7,B 7 u1(01):tag CONSTANT_Utf8
u2(0004):字符串长度4
内容:(6D 61 69 6E)main
7,C 9,4 25 u1(01):tag CONSTANT_Utf8
u2(0016):字符串长度22
内容:(28 5B 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 29 56)([Ljava/lang/String;)V
9,5 9,B 7 u1(01):tag CONSTANT_Utf8
u2(0004):字符串长度4
内容:(61 72 67 73)args
9,C B0,1 22 u1(01):tag CONSTANT_Utf8
u2(0013):字符串长度19
内容:(5B 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B)[Ljava/lang/String;
B0,2 B0,E 13 u1(01):tag CONSTANT_Utf8
u2(000A):字符串长度10
内容:(53 6F 75 72 63 65 46 69 6C 65)SourceFile
B0,F D0,0 18 u1(01):tag CONSTANT_Utf8
u2(000F):字符串长度15
内容:(48 65 6C 6C 6F 57 6F 72 6C 64 2E 6A 61 76 61)HelloWorld.java
D0,1 D0,5 5 u1(0C):CONSTANT_NameAndType
u2(00 07):名字索引7
u2(00 08):描述索引8
D0,6 D0,8 3 u1(07):tag CONSTANT_Class
u2(00 1C):类全限定名索引28
D0,9 D0,D 5 u1(0C):CONSTANT_NameAndType
u2(00 1D):名字索引29
u2(00 1E):描述索引30
D0,E E0,B 14 u1(01):tag CONSTANT_Utf8
u2(00 0B):字符串长度11
内容:(68 65 6C 6C 6F 20 77 6F 72 6C 64)hello world
E0,C E0,E 3 u1(07):tag CONSTANT_Class
u2(00 1F):类全限定名索引31
E0,F F0,3 5 u1(0C):CONSTANT_NameAndType
u2(00 20):名字索引32
u2(00 21):描述索引33
F0,4 100,0 13 u1(01):tag CONSTANT_Utf8
u2(00 0A):字符串长度10
内容:(48 65 6C 6C 6F 57 6F 72 6C 64)HelloWorld
100,1 110,3 19 u1(01):tag CONSTANT_Utf8
u2(00 10):字符串长度16
内容:(6A 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74)java/lang/Object
110,4 120,6 19 u1(01):tag CONSTANT_Utf8
u2(00 10):字符串长度16
内容:(6A 61 76 61 2F 6C 61 6E 67 2F 53 79 73 74 65 6D)java/lang/System
120,7 120,C 6 u1(01):tag CONSTANT_Utf8
u2(00 03):字符串长度3
内容:(6F 75 74)out
120,D 140,4 24 u1(01):tag CONSTANT_Utf8
u2(00 15):字符串长度21
内容:(4C 6A 61 76 61 2F 69 6F 2F 50 72 69 6E 74 53 74 72 65 61 6D 3B)Ljava/io/PrintStream;
140,5 150,A 22 u1(01):tag CONSTANT_Utf8
u2(00 13):字符串长度19
内容:(6A 61 76 61 2F 69 6F 2F 50 72 69 6E 74 53 74 72 65 61 6D)java/io/PrintStream
150,B 160,4 10 u1(01):tag CONSTANT_Utf8
u2(00 07):字符串长度7
内容:(70 72 69 6E 74 6C 6E)println
160,5 170,C 24 u1(01):tag CONSTANT_Utf8
u2(00 15):字符串长度21
内容:(28 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 29 56)(Ljava/lang/String;)V
170,D 170,E 2 u2(00 21):访问标记符ACC_PUBLIC
170,F 180,0 2 u2(00 05):类全限定名,常量池索引5
180,1 180,2 2 u2(00 06):超类类全限定名,常量池索引6
180,3 180,4 2 u2(00 00):接口表数量0
180,5 180,6 2 u2(00 00):成员表数量0
180,7 180,8 2 u2(00 02):方法表数量2
180,9 end u2(00 01):方法访问标记符ACC_PUBLIC
u2(00 07):方法所属类索引7
u2(00 08):方法描述索引8
u2(00 01):方法属性数量1
u2(00 09):属性名字索引9
u4(00 00 00 2F):属性字节长度47
u2(00 01):max_stack1
u2(00 01):max_locals1
u4(00 00 00 05):指令集字节数5
....

至此变成路上HelloWorld的class文件分析的已经擦不多了(小弟道行尚浅,属性表目前几乎还是理解的一塌糊涂,海涵海涵),也算开启了万里长征第一步了,有了对class文件的结构理解,很容易理解ASM框架背后的设计,所以下一篇就是对ASM对比着class文件刨析学习

Supongo que te gusta

Origin juejin.im/post/7073878886563446814
Recomendado
Clasificación