Java 字节码
Java 字节码文件格式
首先了解一下 Java 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];
}
Java 字节码信息说明
名称 | 说明 |
---|---|
magic | 魔数,用于说明这是一个 Java 字节码文件,固定值 0xCAFEBABE |
minor_version, major_version | 字节码文件版本,决定了文件的实际格式信息,对于 Java 1.2 之后版本 Java X,major_version = 44 + X |
constant_pool_count,constant_pool | 常量池信息,常量池索引从 1 到 constant_pool_count – 1 |
access_flags | 类的访问标识 |
this_class,super_class,interfaces_count,interfaces | 类、父类、接口信息,为常量池中索引。super_class 索引为0,则标示为 Object 类。非零,则此类及父类访问标识不能为 ACC_FINAL |
fields_count,fields | 字段信息 |
methods_count,methods | 方法信息 |
attributes_count,attributes | 属性信息,比如源文件名称、行号信息、注解、异常等(详细) |
数据结构说明
数据结构
cp_info {
u1 tag;
u1 info[];
}
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];
}
常量信息tag含义
常量信息中 tag 标识此信息类型,具体如下:
Constant Type | Value |
---|---|
CONSTANT_Class | 7 |
CONSTANT_Fieldref | 9 |
CONSTANT_Methodref | 10 |
CONSTANT_InterfaceMethodref | 11 |
CONSTANT_String | 8 |
CONSTANT_Integer | 3 |
CONSTANT_Float | 4 |
CONSTANT_Long | 5 |
CONSTANT_Double | 6 |
CONSTANT_NameAndType | 12 |
CONSTANT_Utf8 | 1 |
CONSTANT_MethodHandle | 15 |
CONSTANT_MethodType | 16 |
CONSTANT_InvokeDynamic | 18 |
access_flag 对应类型
Flag | Name | Value Interpretation |
---|---|---|
ACC_PUBLIC | 0x0001 | Marked or implicitly public in source. |
ACC_PRIVATE | 0x0002 | Marked private in source. |
ACC_PROTECTED | 0x0004 | Marked protected in source. |
ACC_STATIC | 0x0008 | Marked or implicitly static in source. |
ACC_FINAL | 0x001 | Marked final in source. |
ACC_INTERFACE | 0x0200 | Was an interface in source. |
ACC_ABSTRACT | 0x0400 | Marked or implicitly abstract in source. |
ACC_SYNTHETIC | 0x1000 | Declared synthetic; not present in the source code. |
ACC_ANNOTATION | 0x2000 | Declared as an annotation type. |
ACC_ENUM | 0x4000 | Declared as an enum type. |
示例
Java 代码
创建目录 com/dezng
,其中新建文件 Hello.java
,文件中内容:
package com.dezng;
@Deprecated
public class Hello {
public static void main(String[] args){
System.out.println("Hello");
}
}
javac com/dezng/Hello.java # 编译成功得到 Hello.class 文件
java com.dezng.Hello # 执行输出 Hello 字符
Javap 查看字节码内容
使用 javap -v com.dezng.Hello
Classfile /tmp/000/com/dezng/Hello.class
Last modified 2021-7-2; size 503 bytes
MD5 checksum 9b9bcbe922b43b9e99d78dd059a14565
Compiled from "Hello.java"
public class com.dezng.Hello
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #6.#18 // java/lang/Object."<init>":()V
#2 = Fieldref #19.#20 // java/lang/System.out:Ljava/io/PrintStream;
#3 = String #21 // Hello
#4 = Methodref #22.#23 // java/io/PrintStream.println:(Ljava/lang/String;)V
#5 = Class #24 // com/dezng/Hello
#6 = Class #25 // java/lang/Object
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 main
#12 = Utf8 ([Ljava/lang/String;)V
#13 = Utf8 SourceFile
#14 = Utf8 Hello.java
#15 = Utf8 Deprecated
#16 = Utf8 RuntimeVisibleAnnotations
#17 = Utf8 Ljava/lang/Deprecated;
#18 = NameAndType #7:#8 // "<init>":()V
#19 = Class #26 // java/lang/System
#20 = NameAndType #27:#28 // out:Ljava/io/PrintStream;
#21 = Utf8 Hello
#22 = Class #29 // java/io/PrintStream
#23 = NameAndType #30:#31 // println:(Ljava/lang/String;)V
#24 = Utf8 com/dezng/Hello
#25 = Utf8 java/lang/Object
#26 = Utf8 java/lang/System
#27 = Utf8 out
#28 = Utf8 Ljava/io/PrintStream;
#29 = Utf8 java/io/PrintStream
#30 = Utf8 println
#31 = Utf8 (Ljava/lang/String;)V
{
public com.dezng.Hello();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 4: 0
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String Hello
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 6: 0
line 7: 8
}
SourceFile: "Hello.java"
Deprecated: true
RuntimeVisibleAnnotations:
0: #17()
二进制信息
查看对应二进制信息
对比 javap -v
命令结果查看以上信息
4 段绿色分别对应了 魔术,小版本,主版本(0x34=52,对应 Java 8),常量池数量(32=0x20,对应 javap 中 #1-#31的常量信息)
31 段红蓝交替着色对应了 #1-#31 的常量信息
6 段绿色对应了 访问权限(0x21, ACC_PUBLIC, ACC_SUPER),类信息(#5 com/dezng/Hello),父类信息(#6 Object),接口数量(0),字段数量(0),方法数量(2)
2 段红蓝信息对应方法内容
1 段绿色对应属性数量(3)
3 段红蓝交替对应属性信息
方法二进制解读
以方法区第二个方法查看方法结构,再来看看方法对应数据结构:
method_info {
u2 access_flags; // 0x0009 -> ACC_STATIC,ACC_FINAL
u2 name_index; // 0x000b -> main
u2 descriptor_index; // 0x000c -> ([Ljava/lang/String;)V
u2 attributes_count; // 0x0001
attribute_info attributes[attributes_count];
}
attribute_info {
u2 attribute_name_index; // 0x0009 -> Code
u4 attribute_length; // 0x00000025 -> 37
u1 info[attribute_length];
}
// 根据类型查看结构
Code_attribute {
u2 attribute_name_index; // 0x0009 -> Code
u4 attribute_length; // 0x00000025 -> 37
u2 max_stack; // 0x0002
u2 max_locals; // 0x0001
u4 code_length; // 0x00000009
u1 code[code_length]; // 0xb20002 1203 b60004 b1
u2 exception_table_length;0x0000
{ u2 start_pc;
u2 end_pc;
u2 handler_pc;
u2 catch_type;
} exception_table[exception_table_length];
u2 attributes_count; // 0x0001
attribute_info attributes[attributes_count];// 0x000a -> LineNumberTable
}
LineNumberTable_attribute {
u2 attribute_name_index; // 0x000a -> LineNumberTable
u4 attribute_length; // 0x0000000a
u2 line_number_table_length; // 0x0002
{ u2 start_pc; // 0x0000,0x0008
u2 line_number; // 0x0006,0x0007
} line_number_table[line_number_table_length];
}
文字版:
方法名称:main
方法访问属性:static final
方法参数信息:参数 (String[]),返回 void
方法属性数量:1
方法属性类型:Code
方法属性长度:37(接下来37字节为方法 Code 具体内容,如下)
最大栈:2
本地变量:1
代码长度:9(接下来9字节为字节码指令)
指令内容:0xb20002 1203 b60004 b1(此 9 字节稍后分析)
异常表长度:0
Code属性数量:1
属性类型:LineNumberTable
此部分属性长度:10
此部分属性数量:2
指令位置:0 行号:6
指令位置:8 行号:7
结束
Java 字节码指令
0xb200021203b60004b1 以此9字节做一个简单解读。
0xb2 查文档可知为 getstatic 指令,后接两字节(0x0002)标示常量池中的索引,此索引内容为 Fieldref(java/lang/System.out:Ljava/io/PrintStream;),并将结果放回操作数栈。
0x12 查文档可知为 ldc 指令,后接一个字节(0x03)表示常量池中索引,此索引内容为String(Hello)。指令将代表此常量的引用放到操作数栈。
0xb6 插文档可知为 invokevirtual 指令,后接两字节(0x0004)表示常量池中的索引,此索引的内容为Methodref,指令会调用此方法,将结果放入操作数栈。
0xb1 查文档可知为 return 指令,并且是返回 void,根据方法是否为 synchronized 处理锁,销毁操作数栈和方法帧,接下来由调用者处理。
以上指令即为 main 方法中内容,和 javap 中查看到的 main 方法内容一致。
内容太多了,到此为止,需要了解再补充,主要就是看官方文档。