Jvm Class文件格式、指令
2017-12-07 00:00
148 查看
摘要: 学习《深入理解java虚拟机(第二版)》周志明著 的读书笔记。此篇博客对应书中的第六章内容。
java反汇编及JVM指令集(指令码、助记符、功能描述)
JVM摘要--指令集介绍01
JVM摘要--指令集介绍02
JVM摘要--指令集介绍03
使用java反编译命令:javap -v Test.class(注释:这里只是main方法对应的字节码)
将操作数栈的栈顶一个或两个元素出栈:pop、pop2
将栈最顶端的来两个数值互换:swap
使用javap反编译A$B.class:
使用javap反编译A$B.class:
可以看到,静态内部类的实例化方法A$B();变成无参的了(这里说的无参,是显式无参,即没有LA;类作为参数了,但args_size=1说明参数为this。)
之前接触内部类的时候,有个问题,正好可以用现在学的知识解释:内部类 - 和代总讨论
编译器必须确保无论方法通过何种方式完成,方法中调用过的每条monitorenter指令都必须执行其对应的monitorexit指令,而无论这个方法是正常结束还是异常结束。
——《深入理解java虚拟机(第二版)》周志明著 6.4.10同步指令
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.classjavac 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.classjavac 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文件结构解析(一):Class文件的格式与定义
- JVM之字节码——Class文件格式
- JVM之字节码——Class文件格式
- JVM之字节码——Class文件格式
- JVM规范研读-3 Class文件格式
- JVM加载class文件的原理机制
- JVM加载class文件的原理
- JVM加载class文件的原理机制
- JVM加载class文件的原理机制
- Java9之class文件格式变动
- "java文件-class文件-JVM显示" 编码执行过程
- jvm学习笔记(1)class文件
- JVM加载class文件的原理机制?
- JVM加载class文件的原理机制
- 深入理解Java Class文件格式(五)
- JVM加载class文件的原理机制
- 面试常备题---JVM加载class文件的原理机制
- JVM 解析class文件及 运行时数据区
- 深入理解java语言的class文件格式(五)
- 深入理解Java Class文件格式(一)