package com.dskj.jvm.bytecode;
public class MyTest1 {
private int a = 1;
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
}
Java字节码文件
javap -verbose com.dskj.jvm.bytecode.MyTest1
生成字节码文件内容:
Classfile
/.../classes/com/dskj/jvm/bytecode/MyTest.class
Last modified Jul 31, 2018; size 489 bytes
MD5 checksum bdb537edd2d216ea99d6ce529073ee42
Compiled from "MyTest1.java"
public class com.dskj.jvm.bytecode.MyTest
minor version: 0
major version: 52 # JDK最大版本号
flags: ACC_PUBLIC, ACC_SUPER
Constant pool: #
#1 = Methodref #4.#20 // java/lang/Object."<init>":()V
#2 = Fieldref #3.#21 // com/dskj/jvm/bytecode/MyTest1.a:I
#3 = Class #22 // com/dskj/jvm/bytecode/MyTest1
#4 = Class #23 // java/lang/Object
#5 = Utf8 a
#6 = Utf8 I
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 Lcom/dskj/jvm/bytecode/MyTest1;
#14 = Utf8 getA
#15 = Utf8 ()I
#16 = Utf8 setA
#17 = Utf8 (I)V
#18 = Utf8 SourceFile
#19 = Utf8 MyTest1.java
#20 = NameAndType #7:#8 // "<init>":()V
#21 = NameAndType #5:#6 // a:I
#22 = Utf8 com/dskj/jvm/bytecode/MyTest1
#23 = Utf8 java/lang/Object
{
public com.dskj.jvm.bytecode.MyTest1();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: iconst_1
6: putfield #2 // Field a:I
9: return
LineNumberTable:
line 6: 0
line 8: 4
LocalVariableTable:
Start Length Slot Name Signature
0 10 0 this Lcom/dskj/jvm/bytecode/MyTest1;
public int getA();
descriptor: ()I
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: getfield #2 // Field a:I
4: ireturn
LineNumberTable:
line 11: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/dskj/jvm/bytecode/MyTest1;
public void setA(int);
descriptor: (I)V
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: iload_1
2: putfield #2 // Field a:I
5: return
LineNumberTable:
line 15: 0
line 16: 5
LocalVariableTable:
Start Length Slot Name Signature
0 6 0 this Lcom/dskj/jvm/bytecode/MyTest1;
0 6 1 a I
}
SourceFile: "MyTest1.java”
Java字节码十六进制
Constant pool: #
#1 = Methodref #4.#20 // java/lang/Object."<init>":()V
索引到位置#4和#20,从常量池中找到这两个索引项如下:
#4 = Class #23 // java/lang/Object
#20 = NameAndType #7:#8 // "<init>":()V
这两个索引正好可以跟结构总表中对应上。其中,#4表示的类全限定名为java/lang/Object,而索引20位置又引用了#7:#8。继续找到#7和#8:
#7 = Utf8 <init>
#8 = Utf8 ()V
#2 = Fieldref #3.#21 // com/dskj/jvm/bytecode/MyTest1.a:I
对应有两个索引项#3和#21,如下所示:
#3 = Class #22 // com/dskj/jvm/bytecode/MyTest1
#21 = NameAndType #5:#6 // a:I
索引项#3引用了索引项#22,索引项#21引用了索引项#5:#6
#22 = Utf8 com/dskj/jvm/bytecode/MyTest1
#5 = Utf8 a
#6 = Utf8 I
根据以上,#5表示的变量名为a,#6表示的变量a的返回类型是I,即int类型的。也就知道了#2 = Fileldref,对应的是com/dskj/jvm/bytecode/MyTest1.a:I。
private int a = 1;
从第21位开始的十六进制
#3 = Class #22 // com/dskj/jvm/bytecode/MyTest1
#22 = Utf8 com/dskj/jvm/bytecode/MyTest1
从第27位开始的十六进制
#6 = Utf8 I
十六进制字节码文件:
#6 = Utf8 I
十六进制字节码文件:
01 00 06 3C 69 6E 69 74 3E 01 00 03 28 29 56 01 00 04 43 6F 64 65 01 00 0F 4C 69 6E 65 4E 75 6D 62 65 72 54 61 62 6C 65...
继续查找标志位为01 ,值为1的结构总表常量为CONSTANT_Utf8-info,length的占用2个字节十六进制为 00 06 ,那么length长度就是6(转换为十进制的值,即0 * 16的一次方 + 6),后面找到6个字节为 3C 69 6E 69 74 3E,通过HexFiend工具也能看到指向了<init>。
#7 = Utf8 <init>
以此类推,最终都能通过十六进制字节码并结合字节码结构总表分析在常量池中找到对应的字节码内容。
public class com.dskj.jvm.bytecode.MyTest1
...
flags: ACC_PUBLIC, ACC_SUPER
访问标志之后的是This Class Name,对应十六进制为 0x 00 03
#3 = Class #22 // com/dskj/jvm/bytecode/MyTest1
This Class Name之后的是Super Class Name,对应十六进制为 0x 00 04
#4 = Class #23 // java/lang/Object
Interfaces
field_info {
u2 access_flags; 0002
u2 name_index; 0005
u2 descriptor_index; 0006
u2 attributes_counts; 0000
attribute_info attributes[attributes_count];
}
0x0002在访问标志结构表中对应的是ACC_PRIVATE。
#5 = Utf8 a
#6 = Utf8 I
附加属性的数量为0x0000,转换为十进制为0,后面的附加属性attributes也就不会出现了。
00 03 // methods_count
00 01 // access_flags
00 07 // name_index
00 08 // descriptor_index
00 01 // attributes_count
00 09 // attribute_name_index
00 00 00 38 // attribute_length
00 02 // 附加属性的 max_stacks
00 01 // 附加属性的 max_locals
00 00 00 0A // 附加属性的 code_length
2A B7 00 01 2A 04 B5 00 02 B1 // code_lengthc长度的字节,具体执行的字节码指令
00 00 00 02 00 0A 00 00 00 0A 00 02 00 00 00 06 00 04 00 08 00 0B 00 00 00 0C 00 01 00 00 00 0A 00 0C 00 0D 00 00 00 01 00 0E 00 0F 00 01 00 09 00 00 00 2F 00 01 00 01 00 00 00 05 2A B4 00 02 AC 00 00 00 02 00 0A 00 00 00 06 00 01 00 00 00 0B 00 0B 00 00 00 0C 00 01 00 00 00 05 00 0C 00 0D 00 00 00 01 00 10 00 11 00 01 00 09 00 00 00 3E 00 02 00 02 00 00 00 06 2A 1B B5 00 02 B1 00 00 00 02 00 0A 00 00 00 0A 00 02 00 00 00 0F 00 05 00 10 00 0B 00 00 00 16 00 02 00 00 00 06 00 0C 00 0D 00 00 00 00 00 06 00 05 00 06 00 01 00 01 00 12 00 00 00 02 00 13
#7 = Utf8 <init> // 表示这个类的构造方法
#8 = Utf8 ()V // 表示不接收任何参数的不返回结果的描述符
attributes_count对应十六进制:00 01 ,其个数为1,表示会有一个附加属性。也说明了有一个attributes。
atrribute_info {
u2 atrribute_name_index;
u4 attribute_length;
u1 info[atrribute_length];
}
attribute_name_index对应十六进制为 00 09,在常量池结构表中查找结果:
#9 = Utf8 Code
从字节码中每一个方法中都能体现出来,比如默认构造方法:
public com.dskj.jvm.bytecode.MyTest1();
descriptor: ()V
flags: ACC_PUBLIC
Code:
...
然后根据 atrribute_length 对应十六进制为 00 00 00 38 转换为十进制为3 * 16的一次方 + 8 = 56
method_info {
u2 access_flags;
u2 name_index;
u2 descriptor_index;
u2 attributes_count;
attribute_info attributes[attributes_count]
}
方法的属性结构:
那么,这些十六进制是怎么和下面的助记符对应的呢?
我们通过jclasslib工具(字节码查看工具,支持IDEA插件形式安装)查看时,点击助记符的链接会跳到Oracle官网可查看具体详细解释。第一个助记符: 0: aload_0 打开链接可以看到:
aload_<n>
Operation
Load reference from local variable
Format
aload_<n>
Forms
aload_0 = 42 (0x2a) // 通过这里就能直接看到 aload_0 对应的十进制是42,转换为十六进制就是 0x2a,对应字节码文件中的 2A
aload_1 = 43 (0x2b)
aload_2 = 44 (0x2c)
aload_3 = 45 (0x2d)
Description
The <n> must be an index into the local variable array of the current frame (§2.6). The local variable at <n> must contain a reference. The objectref in the local variable at <n> is pushed onto the operand stack.
这个<n>必须是一个到当前栈帧局部变量数组的一个索引,位于<n>位置上的局部变量会包含一个引用,位于<n>位置上的局部变量的这个引用会被推送到栈顶(准备进行操作)。
第二个助记符:
1: invokespecial #1 // Method java/lang/Object."<init>":()V
连接地址:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.invokespecial
invokespecial
Operation
Invoke instance method; special handling for superclass, private, and instance initialization method invocations
Format
invokespecial
indexbyte1
indexbyte2
Forms
invokespecial = 183 (0xb7)
Operand Stack
..., objectref, [arg1, [arg2 ...]] →
...
具体字节码即是该方法被调用时,虚拟机所执行的字节码。
String getInfoByIdAndName(int id, String name),该方法的描述符为:(I, Ljava/lang/String;)Ljava/lang/String;
Java字节码文件的工具推荐:
热 文 推 荐
☞在以太坊上开发 Dapp 的瓶颈和门槛有哪些?| 博文精选