- A Concurrent Affair - https://www.concurrentaffair.org -

New Synchronized Block Instrumentor

I just finished changing the instrumentor for synchronized blocks. Now it generates bytecode that pretty much exactly matches hand-written Java code. This synchronized block

synchronized(this) {
foo();
}

with the bytecode

aload\_0 // load this
dup
astore\_1 // store in local 1
monitorenter // lock this
aload\_0 // load this
invokevirtual (foo) // call foo()
aload\_1 // restore this from local 1
monitorexit // unlock this
goto 18 // jump to return

// handler for any exception (lines 5-6)
astore\_2 // store exception in local 2
aload\_1 // restore this from local 1
monitorexit // unlock this
aload\_2 // restore exception from local 2
athrow // rethrow

return

gets turned into the bytecode

aload\_0 // load this
invokestatic (SynchronizedMonitor.tryEnter)
aload\_0 // load this
dup
astore\_1 // store in local 1
monitorenter // lock this
aload\_0 // load this
invokestatic (SynchronizedMonitor.enter)
aload\_0 // load this
invokevirtual (foo) // call foo()
aload\_0 // load this
invokestatic (SynchronizedMonitor.leave)
aload\_1 // restore this from local 1
monitorexit // unlock this
goto 26 // jump to return

// handler for any exception (lines 9-10)
astore\_2 // store exception in local 2
aload\_0 // load this
invokestatic (SynchronizedMonitor.leave)
aload\_1 // restore this from local 1
monitorexit // unlock this
aload\_2 // restore exception from local 2
athrow // rethrow

return

which is really close to this bytecode

aload\_0 // load this
invokestatic (SynchronizedMonitor.tryEnter)
aload\_0 // load this
dup
astore\_1 // store this in local 1
monitorenter // lock this
aload\_0 // load this
invokestatic (SynchronizedMonitor.enter)
aload\_0 // load this
invokevirtual (foo) // call foo()
aload\_0 // load this
invokestatic (SynchronizedMonitor.leave)
goto 22 // jump beyond athrow

// exception handler for all exceptions (lines 10-11, 17-18)
astore\_2 // store exception in local 2
aload\_0 // load this
invokestatic (SynchronizedMonitor.leave)
aload\_2 // restore exception from local 2
athrow // rethrow

aload\_1 // restore this from local 1
monitorexit // unlock this
goto 33 // jump to return

// exception handler for all exceptions (lines 8-24, 28-30)
astore\_3 // store exception in local 3
aload\_1 // restore this from local 1
monitorexit // unlick this
aload\_3 // restore exception from local 3
athrow // rethrow

return

which is what javac generates from this Java code:

SynchronizedMonitor.tryEnter(this);
synchronized(this) {
SynchronizedMonitor.enter(this);
try {
foo();
}
finally {
SynchronizedMonitor.leave(this);
}
}

Assuming the monitor methods whose calls have been inserted don’t throw exceptions, I think the instrumented version and the javac version are equivalent. But do I really have to make it this complicated? Is matching the javac output worth it?

I’ve also noticed that javac doesn’t seem to do any optimizations. I’m a bit shocked, but with a JIT compiler it probably makes sense. I actually hope this observation is correct and will remain valid. If javac does make optimizations, then this whole matching idea will immediately go out the door.

I’ll still have to make the synchronized methods to blocks instrumentor work again, and modify all the others… and of course I haven’t tested at all. More work to come, for sure. Good night.

[1] [2]Share [3]