您的位置:首页 > 其它

Jvm Class文件格式、指令

2017-12-07 00:00 148 查看
摘要: 学习《深入理解java虚拟机(第二版)》周志明著 的读书笔记。此篇博客对应书中的第六章内容。

java反汇编及JVM指令集(指令码、助记符、功能描述)

JVM摘要--指令集介绍01

JVM摘要--指令集介绍02

JVM摘要--指令集介绍03

关于JAVA虚拟机的dup指令的一个问题

public class Test {

public static void main(String[] args) {
Test t = new Test();
}
}

使用java反编译命令:javap -v Test.class(注释:这里只是main方法对应的字节码)

C:\Users\AnXiaole\Desktop>javap -v Test.class
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: new           #2        //从内存中申请一块类 class com/hzcf/flagship/web/Test 的对象空间,将空间的地址放入栈顶
3: dup                     // 将第0步得到的类Test 的对象空间地址复制一份,放入栈顶.为什么要复制?这是因为:执行第4步(即下一步)的指令(即:调用Test()构造器时,是需要对象的引用的,即:是用Test()构造器对这个引用所代表的对象空间做初始化的.另外:解析#3时,也要用到它是属于哪个对象的引用.
4: invokespecial #3        // Method "<init>":()V    //执行这一指令时,用到第3步中的引用.执行完成后,第3步的引用已经从栈中弹出了
7: astore_1                // 将此时的栈顶值(正是第0步放入的对象的引用,该引用的对象空间已由3-4两步初始化了)弹出,放入局部变量Test t中.
8: return
LineNumberTable:
line 30: 0
line 31: 8

将操作数栈的栈顶一个或两个元素出栈:pop、pop2

将栈最顶端的来两个数值互换:swap

虚拟机规范预定义的属性

(图来源于:《深入理解java虚拟机(第二版)》周志明著 表6-13)



内部类

非静态内部类B

public class A
{
class B //  非静态内部类
{
}
}


编译

使用javac编译后,生成两个类,一个A.class,另一个是A$B.class

javac A.java


反编译

使用javap反编译A.class:

Microsoft Windows [版本 10.0.14393]
(c) 2016 Microsoft Corporation。保留所有权利。

C:\Users\AnXiaole>cd Desktop

C:\Users\AnXiaole\Desktop>javap -v A.class
Classfile /C:/Users/AnXiaole/Desktop/A.class
Last modified 2017-12-18; size 220 bytes
MD5 checksum 275a40274293fa218e498c7cf8f8939d
Compiled from "A.java"
public class A
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref          #3.#13         // java/lang/Object."<init>":()V
#2 = Class              #14            // A
#3 = Class              #15            // java/lang/Object
#4 = Class              #16            // A$B
#5 = Utf8               B
#6 = Utf8               InnerClasses
#7 = Utf8               <init>
#8 = Utf8               ()V
#9 = Utf8               Code
#10 = Utf8               LineNumberTable
#11 = Utf8               SourceFile
#12 = Utf8               A.java
#13 = NameAndType        #7:#8          // "<init>":()V
#14 = Utf8               A
#15 = Utf8               java/lang/Object
#16 = Utf8               A$B
{
public A();
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 1: 0
}
SourceFile: "A.java"
InnerClasses:
#5= #4 of #2; //B=class A$B of class A

C:\Users\AnXiaole\Desktop>

使用javap反编译A$B.class:

Microsoft Windows [版本 10.0.14393]
(c) 2016 Microsoft Corporation。保留所有权利。

C:\Users\AnXiaole>cd Desktop

C:\Users\AnXiaole\Desktop>javap -v A$B.class
Classfile /C:/Users/AnXiaole/Desktop/A$B.class
Last modified 2017-12-18; size 267 bytes
MD5 checksum ab8de5aa5a0ec80864506f590eac36f3
Compiled from "A.java"
class A$B
minor version: 0
major version: 52
flags: ACC_SUPER
Constant pool:
#1 = Fieldref           #3.#13         // A$B.this$0:LA;
#2 = Methodref          #4.#14         // java/lang/Object."<init>":()V
#3 = Class              #16            // A$B
#4 = Class              #19            // java/lang/Object
#5 = Utf8               this$0
#6 = Utf8               LA;
#7 = Utf8               <init>
#8 = Utf8               (LA;)V
#9 = Utf8               Code
#10 = Utf8               LineNumberTable
#11 = Utf8               SourceFile
#12 = Utf8               A.java
#13 = NameAndType        #5:#6          // this$0:LA;
#14 = NameAndType        #7:#20         // "<init>":()V
#15 = Class              #21            // A
#16 = Utf8               A$B
#17 = Utf8               B
#18 = Utf8               InnerClasses
#19 = Utf8               java/lang/Object
#20 = Utf8               ()V
#21 = Utf8               A
{
final A this$0;                             // javac编译时,编译器自动给非静态内部类B添加了一个字段;
descriptor: LA;                           // 此字段为外部类的类型A;
flags: ACC_FINAL, ACC_SYNTHETIC           // 此字段是final的(0x0010),并且是编译器自动添加出来的(ACC_SYNTHETIC 0x1000);

A$B(A);                                     // B类有一个构造方法,参数为类型A
descriptor: (LA;)V
flags:
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: aload_1
2: putfield      #1                  // Field this$0:LA;为B重的A字段赋值。
5: aload_0
6: invokespecial #2                  // Method java/lang/Object."<init>":()V
9: return
LineNumberTable:
line 3: 0
}
SourceFile: "A.java"
InnerClasses:
#17= #3 of #15; //B=class A$B of class A

C:\Users\AnXiaole\Desktop>


静态内部类B

public class A
{
static class B   // 静态内部类
{
}
}


编译

使用javac编译后,生成两个类,一个A.class,另一个是A$B.class

javac A.java


反编译

先使用javac编译(同上)。再使用javap反编译A.class:

Microsoft Windows [版本 10.0.14393]
(c) 2016 Microsoft Corporation。保留所有权利。

C:\Users\AnXiaole>cd Desktop

C:\Users\AnXiaole\Desktop>javap -v A.class
Classfile /C:/Users/AnXiaole/Desktop/A.class
Last modified 2017-12-18; size 220 bytes
MD5 checksum 8e47700d8b726d5939db637ebd069fd8
Compiled from "A.java"
public class A
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref          #3.#13         // java/lang/Object."<init>":()V
#2 = Class              #14            // A
#3 = Class              #15            // java/lang/Object
#4 = Class              #16            // A$B
#5 = Utf8               B
#6 = Utf8               InnerClasses
#7 = Utf8               <init>
#8 = Utf8               ()V
#9 = Utf8               Code
#10 = Utf8               LineNumberTable
#11 = Utf8               SourceFile
#12 = Utf8               A.java
#13 = NameAndType        #7:#8          // "<init>":()V
#14 = Utf8               A
#15 = Utf8               java/lang/Object
#16 = Utf8               A$B
{
public A();
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 1: 0
}
SourceFile: "A.java"
InnerClasses:
static #5= #4 of #2; //B=class A$B of class A

C:\Users\AnXiaole\Desktop>

使用javap反编译A$B.class:

Microsoft Windows [版本 10.0.14393]
(c) 2016 Microsoft Corporation。保留所有权利。

C:\Users\AnXiaole>cd Desktop

C:\Users\AnXiaole\Desktop>javap -v A$B.class
Classfile /C:/Users/AnXiaole/Desktop/A$B.class
Last modified 2017-12-18; size 220 bytes
MD5 checksum 0e5880806b58919d834d99aa3edeb341
Compiled from "A.java"
class A$B
minor version: 0
major version: 52
flags: ACC_SUPER
Constant pool:
#1 = Methodref          #3.#10         // java/lang/Object."<init>":()V
#2 = Class              #12            // A$B
#3 = Class              #15            // java/lang/Object
#4 = Utf8               <init>
#5 = Utf8               ()V
#6 = Utf8               Code
#7 = Utf8               LineNumberTable
#8 = Utf8               SourceFile
#9 = Utf8               A.java
#10 = NameAndType        #4:#5          // "<init>":()V
#11 = Class              #16            // A
#12 = Utf8               A$B
#13 = Utf8               B
#14 = Utf8               InnerClasses
#15 = Utf8               java/lang/Object
#16 = Utf8               A
{
A$B();
descriptor: ()V
flags:
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
}
SourceFile: "A.java"
InnerClasses:
static #13= #2 of #11; //B=class A$B of class A

C:\Users\AnXiaole\Desktop>

可以看到,静态内部类的实例化方法A$B();变成无参的了(这里说的无参,是显式无参,即没有LA;类作为参数了,但args_size=1说明参数为this。)

之前接触内部类的时候,有个问题,正好可以用现在学的知识解释:内部类 - 和代总讨论

问题

关于同步指令的一些疑问

同步一段指令集序列通常使用Java语言中的synchronized语句块来表示的,Java虚拟机的指令集中有monitorenter和monitorexit两条指令来支持synchronized关键字的语义。

编译器必须确保无论方法通过何种方式完成,方法中调用过的每条monitorenter指令都必须执行其对应的monitorexit指令,而无论这个方法是正常结束还是异常结束。

——《深入理解java虚拟机(第二版)》周志明著 6.4.10同步指令

public class Test {

public void doSomething(String args) {
synchronized (args) {
System.out.println(111);
}
}
}

Microsoft Windows [版本 10.0.14393]
(c) 2016 Microsoft Corporation。保留所有权利。

C:\WorkSpaces>javap -v Test.class
Classfile /C:/WorkSpaces/Test.class
Last modified 2017-12-18; size 542 bytes
MD5 checksum 2cdcfdd7b89ea6adce87e50e042c739b
Compiled from "Test.java"
public class com.huizhongcf.util.Test
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref          #5.#19         // java/lang/Object."<init>":()V
#2 = Fieldref           #20.#21        // java/lang/System.out:Ljava/io/PrintStream;
#3 = Methodref          #22.#23        // java/io/PrintStream.println:(I)V
#4 = Class              #24            // com/huizhongcf/util/Test
#5 = Class              #25            // java/lang/Object
#6 = Utf8               <init>
#7 = Utf8               ()V
#8 = Utf8               Code
#9 = Utf8               LineNumberTable
#10 = Utf8               doSomething
#11 = Utf8               (Ljava/lang/String;)V
#12 = Utf8               StackMapTable
#13 = Class              #24            // com/huizhongcf/util/Test
#14 = Class              #26            // java/lang/String
#15 = Class              #25            // java/lang/Object
#16 = Class              #27            // java/lang/Throwable
#17 = Utf8               SourceFile
#18 = Utf8               Test.java
#19 = NameAndType        #6:#7          // "<init>":()V
#20 = Class              #28            // java/lang/System
#21 = NameAndType        #29:#30        // out:Ljava/io/PrintStream;
#22 = Class              #31            // java/io/PrintStream
#23 = NameAndType        #32:#33        // println:(I)V
#24 = Utf8               com/huizhongcf/util/Test
#25 = Utf8               java/lang/Object
#26 = Utf8               java/lang/String
#27 = Utf8               java/lang/Throwable
#28 = Utf8               java/lang/System
#29 = Utf8               out
#30 = Utf8               Ljava/io/PrintStream;
#31 = Utf8               java/io/PrintStream
#32 = Utf8               println
#33 = Utf8               (I)V
{
public com.huizhongcf.util.Test();
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 12: 0

public void doSomething(java.lang.String);
descriptor: (Ljava/lang/String;)V
flags: ACC_PUBLIC
Code:
stack=2, locals=4, args_size=2
0: aload_1                           //将参数args对象入栈
1: dup                               // 复制栈顶元素(即args的引用)
2: astore_2                          // 将栈顶元素存储到局变量表slot 2中(*作用:如果出现异常,那么栈顶就会放入异常对象。由于出现异常的时也要释放锁,所以先把锁放到slot 2中,出了异常的时候先从slot 2中获得锁然后释放掉锁monitorexit)
3: monitorenter                      // 以栈顶元素为锁,开始同步
4: getstatic     #2                  // 此三个指令用于调用System.out.println(111);
7: bipush        111                 // 此三个指令用于调用System.out.println(111);
9: invokevirtual #3                  // 此三个指令用于调用System.out.println(111);
12: aload_2                           // 将局部变量slot 2的元素(即args)入栈
13: monitorexit                       // 退出同步
14: goto          22                  // 方法正常结束,找转到22 return
17: astore_3                          // 从这部开始是异常路径,见下面异常表的Target 13
18: aload_2                           // 将局部变量slot 2的元素(即args)入栈
19: monitorexit                       // 退出同步
20: aload_3                           // 将局部变量slot 3的元素(即异常对象)入栈
21: athrow                            // 把异常对象重新抛给此方法的调用者
22: return                            // 方法正常返回
Exception table:
from    to  target type
4    14    17   any              // 如果从第4行(包含)到第14行(不报含)出现any异常,则跳转到第17行继续执行
17    20    17   any              // 如果从第17行(包含)到第14行(不报含)出现any异常,则跳转到第17行继续执行
LineNumberTable:
line 15: 0
line 16: 4
line 17: 12
line 18: 22
StackMapTable: number_of_entries = 2
frame_type = 255 /* full_frame */
offset_delta = 17
locals = [ class com/huizhongcf/util/Test, class java/lang/String, class java/lang/Object ]
stack = [ class java/lang/Throwable ]
frame_type = 250 /* chop */
offset_delta = 4
}
SourceFile: "Test.java"


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  jvm class