Synchronized Block 能否简化为字节码级别的 Try-Finally Block?

Can a Synchronized Block be simplified to a Try-Finally Block on the Bytecode Level?

正在为类似 Java 的语言编写我自己的编译器,我在编译 synchronized blocks 时遇到了问题。我想出了以下想法将它们简化为 try-finally 个块:

synchonized (obj) {
     statements...
}

可以替换为

Object _lock = obj
_monitorEnter(lock)
try {
    statements...
}
finally {
    _monitorExit(lock)
}

其中_monitorEnter_monitorExit代表MONITORENTERMONITOREXIT指令。

我对 synchronized 的编译方式的假设是否正确,还是我遗漏了什么?

编辑

我的实现之前对正文中的 returnthrow 语句进行了一些特殊处理。基本上,它会在每个 *RETURNTHROW 指令之前手动加载所有 lock 变量和 MONITOREXIT 它们。这是由 finally 块处理的,还是我还需要这些检查?

你的假设是正确的。 Java语言中的synchronized块是用monitorentermonitorexit指令实现的。您可以查看 JVM 规范详细信息 here.

Synchronization in the Java Virtual Machine is implemented by monitor entry and exit, either explicitly (by use of the monitorenter and monitorexit instructions) or implicitly (by the method invocation and return instructions).

编译器生成的字节码将处理 synchronized 体内抛出的所有异常,因此您的 try-finally 方法在这里可以正常工作。

Specification of finally 语句没有说明任何关于释放监视器的信息。第一个 link 中提供的示例显示了包装在 synchronized 块中的简单方法的字节码。如您所见,处理任何可能的异常以确保 monitorexit 指令执行。您应该在编译器中实现相同的行为(编写将在 finally 语句中释放监视器的代码)。

void onlyMe(Foo f) {
    synchronized(f) {
        doSomething();
    }
}

Method void onlyMe(Foo)
0   aload_1             // Push f
1   dup                 // Duplicate it on the stack
2   astore_2            // Store duplicate in local variable 2
3   monitorenter        // Enter the monitor associated with f
4   aload_0             // Holding the monitor, pass this and...
5   invokevirtual #5    // ...call Example.doSomething()V
8   aload_2             // Push local variable 2 (f)
9   monitorexit         // Exit the monitor associated with f
10  goto 18             // Complete the method normally
13  astore_3            // In case of any throw, end up here
14  aload_2             // Push local variable 2 (f)
15  monitorexit         // Be sure to exit the monitor!
16  aload_3             // Push thrown value...
17  athrow              // ...and rethrow value to the invoker
18  return              // Return in the normal case
Exception table:
From    To      Target      Type
4       10      13          any
13      16      13          any

Java 编译器将同步块编译成类似于 try-finally 的东西,如您所猜。但是,有一个细微差别——异常处理捕获 monitorexit 抛出的异常并无限循环尝试释放锁。在 Java.

中无法像这样指定控制流