以下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
答案 0 :(得分: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 specs:
通常,Java编程语言的编译器可以确保这一点 由执行的监视器指令实现的锁定操作 在执行synchronized语句的主体之前是 由monitorexit实现的解锁操作匹配 是否同步语句完成时的指令 完成是正常的或突然的。
答案 1 :(得分: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");
}
我相信最后可能会丢失一个catch块表。