Search code examples
javabytecodesynchronizedtry-finallyjvm-bytecode

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


Writing my own compiler for a Java-like language, I am having trouble compiling synchronized blocks. I come up with the following idea to simplify them to try-finally blocks:

synchonized (obj) {
     statements...
}

Can be replaced with

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

Where _monitorEnter and _monitorExit represent the MONITORENTER and MONITOREXIT instructions.

Am I correct with this assumption of how synchronized is compiled, or am I missing something?

EDIT

My implementation previously had some special handling for return and throw statements within the body. Basically, it would manually load all lock variables and MONITOREXIT them before each *RETURN or THROW instruction. Is this handled by the finally block, or do I still need these checks?


Solution

  • Your assumptions are correct. The synchronized block in the Java language is implemented with monitorenter and monitorexit instructions. You can review the JVM specification details 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).

    The compiler generates bytecode that will handle all exceptions thrown inside a synchronized body, so your try-finally approach will work fine here.

    Specification of finally statement does not tell anything about releasing monitors. Example provided in the first link shows bytecode for a simple method wrapped in synchronized block. As you can see, any possible exception is handled to ensure monitorexit instruction execution. You should implement same behaviour in your compiler (write code that will release monitor inside finally statement).

    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