7

以下 Java 代码生成以下 JVM 字节码。

我很好奇为什么会生成偏移量 31 到偏移量 36 的代码。JLS7 或 JVM7 规范中没有提到这一点。我错过了什么吗?

即使我删除了 println 语句,代码(偏移量 31 到偏移量 36)仍然会生成,只是在较早的位置,因为 println 调用已被删除。

// Java code
    void testMonitor() {
        Boolean x = new Boolean(false);
        synchronized(x) {
            System.out.println("inside synchronized");
            System.out.println("blah");
        };
        System.out.println("done");
    }


// JVM bytecode
    Offset  Instruction       Comments (Method: testMonitor)
    0       new 42            (java.lang.Boolean)
    3       dup
    4       iconst_0
    5       invokespecial 44  (java.lang.Boolean.<init>)
    8       astore_1          (java.lang.Boolean x)
    9       aload_1           (java.lang.Boolean x)
    10      dup
    11      astore_2
    12      monitorenter
    13      getstatic 15      (java.lang.System.out)
    16      ldc 47            (inside synchronized)
    18      invokevirtual 23  (java.io.PrintStream.println)
    21      getstatic 15      (java.lang.System.out)
    24      ldc 49            (blah)
    26      invokevirtual 23  (java.io.PrintStream.println)
    29      aload_2
    30      monitorexit
    31      goto 37
    34      aload_2
    35      monitorexit
    36      athrow
    37      getstatic 15      (java.lang.System.out)
    40      ldc 51            (done)
    42      invokevirtual 23  (java.io.PrintStream.println)
    45      return
4

2 回答 2

7

编译器在此处添加了一个不可见的 try/catch 块,以确保释放监视器状态(在 VM 规范中记录,请参阅帖子底部)。您可以通过使用javap -v并查看异常表来验证这一点:

void testMonitor();
  Code:
   Stack=3, Locals=3, Args_size=1
   0:    new    #15; //class java/lang/Boolean
   3:    dup
   4:    iconst_0
   5:    invokespecial    #17; //Method java/lang/Boolean."<init>":(Z)V
   8:    astore_1
   9:    aload_1
   10:    dup
   11:    astore_2
   12:    monitorenter
   13:    getstatic    #20; //Field java/lang/System.out:Ljava/io/PrintStream;
   16:    ldc    #26; //String inside synchronized
   18:    invokevirtual    #28; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   21:    getstatic    #20; //Field java/lang/System.out:Ljava/io/PrintStream;
   24:    ldc    #34; //String blah
   26:    invokevirtual    #28; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   29:    aload_2
   30:    monitorexit
   31:    goto    37
   34:    aload_2
   35:    monitorexit
   36:    athrow
   37:    getstatic    #20; //Field java/lang/System.out:Ljava/io/PrintStream;
   40:    ldc    #36; //String done
   42:    invokevirtual    #28; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   45:    return
  Exception table:
   from   to  target type
    13    31    34   any
    34    36    34   any

编辑:来自JVM 规范

通常,Java 编程语言的编译器会确保在执行同步语句的主体之前执行的 monitorenter 指令实现的锁定操作与每当同步语句完成时由 monitorexit 指令实现的解锁操作匹配,无论是否完成是正常的还是突然的。

于 2012-03-31T14:34:35.010 回答
2

我不知道它在 JLS 中的哪个位置,但它必须在某处说,当抛出异常时,释放锁。您可以使用 Unsafe.monitorEnter/Exit 执行此操作

void testMonitor() {
    Boolean x = new Boolean(false);
    theUnsafe.monitorEnter(x);
    try {
        System.out.println("inside synchronized");
        System.out.println("blah");
    } catch(Throwable t) {
        theUnsafe.monitorExit(x);
        throw t;
    };
    theUnsafe.monitorExit(x);
    System.out.println("done");
}

我相信最后你可能会丢失一个捕获块表。

于 2012-03-31T14:33:39.470 回答