As far as I can remember, "synchronized" essentially flushes all cached values on exit, and ensures reading all values from their main location on entry. But I wonder how that can be accomplished? I mean, when the block only contains reading some locals, it's easy to know that only those locals are used in the block. However what about { x.f(); } - who knows what things it reads? The call even might be polymorphic, making JIT unable to guess what method will be called, or it might involve reflection. Then who knows if the method reads from some cached location rather than the main memory?
So, how can JVM know which values to flush/refresh when entering/exiting "synchronized" block? Or know which HB relationships need to be established, anyway? (i.e. know what data is accessed by those blocks?)
I only see one way of achieving the required behavior without disabling caching altogether: any method must read from main location on its entry (and can only cache values local to its body), this way no special processing is necessary for any method calls from synchronized block to make sure those calls don't read from caches.
I'd like to know if that is how it's implemented in some specific JVM (say Azul's) or maybe they use some other approach that I am somehow missing.
UPDATE. I already know that's not quite how Zulu works: in my test, f(x) does not seem to be re-read from main memory on every call to it. I tested it w/o "synchronized" and the external changes weren't visible to the method. But I believe it's because the method was actually inlined not called, or maybe because my code was so simple that JIT was able to deduce that it does not request any HB for any data accessed in that method. So maybe my guess is still correct - but only unless such optimizations are used.