用notepad++打开,需要安装一个HEX-Editor插件。这里有个小坑,必须是装32位的notepad,然后打开用插件下面的HEX-Edito—>View in HEx就好了!
或者用editplus,notepad++复制出来格式不对。最好用这个
字节码文件解析流程图,从上到小解析就好了
其实解析字节码就想摩斯密码一样,有一个对应的密码本。对照着就可以解析了。具体可以看书《JVM虚拟机规范》
u1的意思是: 占1个字节 (16进制的一个字节占两位00)
u2的意思是: 占2个字节
u4的意思是: 占4个字节
!的意思是:不确定,是动态计算的
首先说下:上面的字节码都是16进制的,需要计算成10进制的,其次可以看到分成三部分,左边address部分和右边dump部分是没有用的,我们直接解析中间的。还有并不是一排表示一个东西。最后我会把我解析的这个类的信息贴出来。
ca fe ba be:魔术,表示是一个合格的class文件
为什么会有魔数?魔数有什么用?
其实就是JVM判断是不是一个合格的class文件,如果是就继续往下面读
00 00:次版本号。这个没啥好解释的把
00 34:主版本号 (3*15+4=52)= jdk1.8
00 20 =32。这里需要注意常量池是从1开始的,所以要减一,理论上是32个,实际是31个。具百度说,0是null。看图验证:工具是jclasslib,idea的插件
常量池选项属性图。 下面有些属性我有解释,但是具体的解释最好还是看虚拟机规范
Methodref_info {
u1 tag; 0A=10
u2 class_index; 00 05 指向常量池5
u2 name_and_type_index; 00 1A 它表示当前字段或方法的名字和描述符
}
Fieldref_info {
u1 tag; 09
u2 class_index; 00 04
u2 name_and_type_index; 00 1B
}
Fieldref_info {
u1 tag; 09
u2 class_index; 00 04
u2 name_and_type_index; 00 1C
}
00 21 = public 和 super
00 05 指向常量池,父类
00 00 :0那么就跳过interfaces[]
00 02 表示该类或接口声明的类字段或者实例字段个数,说白了就是你的成员变量有几个
fields_info[
field_info {
u2 access_flags; 00 08 access_flags 项的值是用于定义字段被访问权限和基础属性的掩码标志 请对表
u2 name_index; 00 06 指向常量池
u2 descriptor_index; 00 07 描述符
u2 attributes_count; 00 00
attribute_info attributes[attributes_count]; 就是如果上面哪个count不为0,这里又是一个数组
}
field_info {
u2 access_flags; 00 08
u2 name_index; 00 08
u2 descriptor_index; 00 07
u2 attributes_count; 00 00
attribute_info attributes[attributes_count];
}
]
00 03 =3个方法个数 应该会好奇为啥会是三个哈?我这明明只写了一个。因为我本身main有一个,构造函数有一个,clint也是一个。clint这个方法是动态生成的,如果类中有静态属性或者静态块就会生成这个方法
methods_info[
method_info {
u2 access_flags; 00 01 access_flags 项的值是用于定义当前方法的访问权限和基本属性的掩码标志
u2 name_index; 00 09 指向常量池
u2 descriptor_index; 00 0A 方法描述符 V代表返回值是void 其他跟字段描述符一样
u2 attributes_count; 00 01 code属性个数
attribute_info attributes[attributes_count];
attributes[
Code_attribute {
u2 attribute_name_index; 00 0B 指向常量池,表示字符串code
u4 attribute_length; 00 00 00 2F attribute_length这个意思是下面所有的字节数加起来是这个数;attribute_length 项的值表示当前属性的长度,不包括开始的 6 个字节
u2 max_stack; 00 01 操作栈数量,是栈帧里面的 在任意时刻,操作数栈都会有一个确定的栈深度,一个 long 或者 double 类型的数据会占用两个单位的栈深度,其他数据类型则会占用一个单位深度
u2 max_locals; 00 01 max_locals 项的值给出了分配在当前方法引用的局部变量表中的局部变量个数
u4 code_length; 00 00 00 05 code_length 项给出了当前方法的 code[]数组的字节数
u1 code[code_length]; 2A B7 00 01 B1 code[]数组给出了实现当前方法的 Java 虚拟机字节码---方法体或者说是字节码指令
u2 exception_table_length; 00 00 异常
{ u2 start_pc;
u2 end_pc;
u2 handler_pc;
u2 catch_type;
} exception_table[exception_table_length];
u2 attributes_count; 00 02 表示attributes数组的个数
attribute_info attributes[attributes_count];
attributes[
LineNumberTable_attribute { 为什么我知道是这个属性勒?因为00 0C指向常量池12,而12就是LineNumberTable_attribute。
u2 attribute_name_index; 00 0C
u4 attribute_length; 00 00 00 06 attribute_length 给出了当前属性的长度,不包括开始的 6 个字节
u2 line_number_table_length; 00 01 说明line_number_table数组的个数,而line_number_table里的元素就是下面的这个{}
line_number_table[line_number_table_length];
[
{
u2 start_pc; 00 00 code数组的索引
u2 line_number; 00 08 line_number 项的值必须与源文件的行数相匹配
}
]
}
LocalVariableTable_attribute {
u2 attribute_name_index; 00 0D 指向常量池
u4 attribute_length; 00 00 00 0c 当前属性的长度,不包括开始的 6 个字节
u2 local_variable_table_length; 00 01 local_variable_table[]数组的成员个数
local_variable_table[local_variable_table_length];
[
{
u2 start_pc; 00 00 start_pc是local_variable_table的下标
u2 length; 00 05 length两种意思。具体的看书
u2 name_index; 00 0E 指向常量池,表示一个局部变量的有效的非全限定名
u2 descriptor_index; 00 0F 字段描述符
u2 index; 00 00 index为此局部变量在当前栈帧的局部变量表中的索引
}
]
}
]
}
]
}
method_info {
u2 access_flags; 00 09 public static 修饰符 可以相加的
u2 name_index; 00 10
u2 descriptor_index; 00 11
u2 attributes_count; 00 03
attribute_info attributes[attributes_count];
attributes[
Code_attribute {
u2 attribute_name_index; 00 0B 指向常量池,表示字符串code
u4 attribute_length; 00 00 00 2B attribute_length这个意思是下面所有的字节数加起来是这个数;attribute_length 项的值表示当前属性的长度,不包括开始的 6 个字节
u2 max_stack; 00 00 操作栈数量,是栈帧里面的 在任意时刻,操作数栈都会有一个确定的栈深度,一个 long 或者 double 类型的数据会占用两个单位的栈深度,其他数据类型则会占用一个单位深度
u2 max_locals; 00 01 max_locals 项的值给出了分配在当前方法引用的局部变量表中的局部变量个数
u4 code_length; 00 00 00 01 code_length 项给出了当前方法的 下面的code[]数组的字节数
u1 code[code_length]; B1 code[]数组给出了实现当前方法的 Java 虚拟机字节码---方法体或者说是字节码指令
u2 exception_table_length; 00 00 异常
{ u2 start_pc;
u2 end_pc;
u2 handler_pc;
u2 catch_type;
} exception_table[exception_table_length];
u2 attributes_count; 00 02 表示attributes数组的个数
attribute_info attributes[attributes_count];
attributes[
LineNumberTable_attribute { 为什么我知道是这个属性勒?因为00 0C指向常量池12,而12就是LineNumberTable_attribute。
u2 attribute_name_index; 00 0C
u4 attribute_length; 00 00 00 06 attribute_length 给出了当前属性的长度,不包括开始的 6 个字节
u2 line_number_table_length; 00 01 说明line_number_table数组的个数,而line_number_table里的元素就是下面的这个{}
line_number_table[line_number_table_length];
[
{
u2 start_pc; 00 00 code数组的索引
u2 line_number; 00 10 line_number 项的值必须与源文件的行数相匹配
}
]
}
LocalVariableTable_attribute {
u2 attribute_name_index; 00 0D 指向常量池
u4 attribute_length; 00 00 00 0c 当前属性的长度,不包括开始的 6 个字节
u2 local_variable_table_length; 00 01 local_variable_table[]数组的成员个数
local_variable_table[local_variable_table_length];
[
{
u2 start_pc; 00 00 start_pc是local_variable_table的下标
u2 length; 00 01 length两种意思。具体的看书
u2 name_index; 00 12 指向常量池,表示一个局部变量的有效的非全限定名
u2 descriptor_index; 00 13 字段描述符
u2 index; 00 00 index为此局部变量在当前栈帧的局部变量表中的索引
}
]
}
]
},
Exceptions_attribute {
u2 attribute_name_index; 00 14 指向常量池20,代表字符串exceptions
u4 attribute_length; 00 00 00 04 attribute_length 项的值给出了当前属性的长度,不包括开始的 6 个字节
u2 number_of_exceptions; 00 01 exception_index_table数组成员个数
u2 exception_index_table[number_of_exceptions];
[
Class_info {
u1 tag; 默认等于00 07
u2 name_index; 00 15 指向常量池
}
]
},
{ 这个属性我没找到 methodParamters
00 16 00 00 00 05 01 00 12 00 00属于methodparameters
}
]
}
method_info { 第三个不解析了太累了
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
]
00 01 类属性
attributes[attributes_count] = attributes[1]
[
SourceFile_attribute {
u2 attribute_name_index; 00 18
u4 attribute_length; 00 00 00 02
u1 sourcefile_index; 00 19
}
]
field_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
method_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count];
}
attribute_info {
u2 attribute_name_index;
u4 attribute_length;
u1 info[attribute_length];
}
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];
}
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];
}
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];
}
SourceFile_attribute {
u2 attribute_name_index;
u4 attribute_length;
u2 sourcefile_index;
}
Java虚拟机规范pdf带目录