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
代表MONITORENTER
和MONITOREXIT
指令。
我对 synchronized
的编译方式的假设是否正确,还是我遗漏了什么?
编辑
我的实现之前对正文中的 return
和 throw
语句进行了一些特殊处理。基本上,它会在每个 *RETURN
或 THROW
指令之前手动加载所有 lock
变量和 MONITOREXIT
它们。这是由 finally
块处理的,还是我还需要这些检查?
你的假设是正确的。 Java语言中的synchronized
块是用monitorenter
和monitorexit
指令实现的。您可以查看 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.
中无法像这样指定控制流
正在为类似 Java 的语言编写我自己的编译器,我在编译 synchronized blocks
时遇到了问题。我想出了以下想法将它们简化为 try-finally
个块:
synchonized (obj) {
statements...
}
可以替换为
Object _lock = obj
_monitorEnter(lock)
try {
statements...
}
finally {
_monitorExit(lock)
}
其中_monitorEnter
和_monitorExit
代表MONITORENTER
和MONITOREXIT
指令。
我对 synchronized
的编译方式的假设是否正确,还是我遗漏了什么?
编辑
我的实现之前对正文中的 return
和 throw
语句进行了一些特殊处理。基本上,它会在每个 *RETURN
或 THROW
指令之前手动加载所有 lock
变量和 MONITOREXIT
它们。这是由 finally
块处理的,还是我还需要这些检查?
你的假设是正确的。 Java语言中的synchronized
块是用monitorenter
和monitorexit
指令实现的。您可以查看 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.