1. Class文件结构概述
源代码经过编译器之后便会生成一个字节码文件,字节码是一种二进制的类文件,它的内容是JVM的字节码指令,而不像C,C++经过编译器直接生成机器码。
本篇主要描述Class文件的格式,Java虚拟机规定用u1,u2,u4三种结构来表示1,2,4字节无符号整数,相同类型的若干条数据集合用表的形式来存储。表是一个变长的结构,由代表长度的表头n和紧随着n个数据项组成。Class文件采用类似C语言的结构体来存储数据。如下:
一个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];
}
参考官网文档:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.1
2. 什么是字节码指令
Java虚拟机的指令由一个字节长度的,代表这某种特点含义的操作码(opcode)以及跟随气候的零至多个代表操作所需要的参数的操作数(operand)所构成。虚拟机中许多指令并不包含操作数,只有一个操作码。比如:
比如:istore_1 就只有操作码,没有操作数,而istore 4就是有操作码 与操作数构成的一条指令。
istore_1代表将一个整形类型的变量存储在局部变量表下标为1的位置上;
istore 4代表将一个整形类型的变量存储在局部变量表下标为4的位置上;
3.如何解读二进制字节码
子曰:“工欲善其事必先利其器”
3.1. Notepad++ 安装HEX-Editor插件方式查看
这种方式查看到的就是最原始的字节码,每一个都是16进制的数据。
3.2. javap命令方式查看(jdk自带的的反解析工具)
D:\IdeaProjects\MyDemo\target\classes\org\example>javap -v OpCode.class
Classfile /D:/IdeaProjects/MyDemo/target/classes/org/example/OpCode.class
Last modified 2023-6-9; size 606 bytes
MD5 checksum d0123aeb80b8944eb81d21f38828c1eb
Compiled from "OpCode.java"
public class org.example.OpCode
minor version: 0
major version: 51
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #5.#24 // java/lang/Object."<init>":()V
#2 = Fieldref #25.#26 // java/lang/System.out:Ljava/io/PrintStream;
#3 = Methodref #27.#28 // java/io/PrintStream.println:(I)V
#4 = Class #29 // org/example/OpCode
#5 = Class #30 // java/lang/Object
#6 = Utf8 <init>
#7 = Utf8 ()V
#8 = Utf8 Code
#9 = Utf8 LineNumberTable
#10 = Utf8 LocalVariableTable
#11 = Utf8 this
#12 = Utf8 Lorg/example/OpCode;
#13 = Utf8 main
#14 = Utf8 ([Ljava/lang/String;)V
#15 = Utf8 args
#16 = Utf8 [Ljava/lang/String;
#17 = Utf8 a
#18 = Utf8 I
#19 = Utf8 b
#20 = Utf8 c
#21 = Utf8 d
#22 = Utf8 SourceFile
#23 = Utf8 OpCode.java
#24 = NameAndType #6:#7 // "<init>":()V
#25 = Class #31 // java/lang/System
#26 = NameAndType #32:#33 // out:Ljava/io/PrintStream;
#27 = Class #34 // java/io/PrintStream
#28 = NameAndType #35:#36 // println:(I)V
#29 = Utf8 org/example/OpCode
#30 = Utf8 java/lang/Object
#31 = Utf8 java/lang/System
#32 = Utf8 out
#33 = Utf8 Ljava/io/PrintStream;
#34 = Utf8 java/io/PrintStream
#35 = Utf8 println
#36 = Utf8 (I)V
{
public org.example.OpCode();
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 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lorg/example/OpCode;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=5, args_size=1
0: bipush 10
2: istore_1
3: bipush 10
5: istore_2
6: bipush 10
8: istore_3
9: bipush 10
11: istore 4
13: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
16: iload_1
17: iload_2
18: iadd
19: iload_3
20: iadd
21: iload 4
23: iadd
24: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
27: return
LineNumberTable:
line 5: 0
line 6: 3
line 7: 6
line 8: 9
line 9: 13
line 10: 27
LocalVariableTable:
Start Length Slot Name Signature
0 28 0 args [Ljava/lang/String;
3 25 1 a I
6 22 2 b I
9 19 3 c I
13 15 4 d I
}
以上看到的字节码结构,已经是被翻译整理过后的结果,相比用notepad++看到的结果,可以清晰看到Class文件的组成结构。
3.3. idea插件jclasslib方式查看
与javap解析后看到的结果类似,好处就是idea自带的插件,使用方便。
3.4. jclasslib bytecode viewer客户端查看
与使用Idea jclasslib插件方式一致,区别是jclasslib单独为客户端提供的工具。