Jvm Class文件格式、指令

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)

154602_YoiJ_2475326.png

 

内部类

    非静态内部类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"

202827_IffU_2475326.png

 

 

 

 

 

 

 

转载于:https://my.oschina.net/anxiaole/blog/1586402

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值