This Answer is solely to address a point raised in this comment:
It would be illegal for the compiler to generate the first case the same as the second. Where a method call appears in the source, it must be evaluated. There is, after all, no guaranteed that getX returns the same value each time, nor is there a guarantee that getX does not modify some internal value in a. – Hot Licks Jun 28 at 11:55
Here's the code at issue:
if(a.getX() < 0) {
y = a.getX();
}
where getX()
is
public int getX(){return x;}
(This method is clearly side-effect free.)
In fact, the compiler is allowed to optimize the 2nd call away, assuming that it can deduce that nothing in the current thread will can alter the result. It is allowed to ignore changes made by another thread ... unless there is the action that made the relevant state change "happens before" the action that observed the state. (In other words, unless the change is made in a thread-safe fashion.)
In this case, the code is clearly not thread-safe. And it therefore follows that the compiler (or more precisely, the JIT compiler) is permitted to optimize away the 2nd call.
However, the bytecode compiler is not permitted to make this optimization. The two classes are separate compilation units, and the bytecode compiler has to allow for the possibility that (say) A
could be modified and recompiled after B
has been recompiled. Thus, the bytecode compiler cannot be certain that A.getX()
is always going to be side-effect free while compiling B
. (By contrast, the JIT can make this deduction ... since the classes can't change after they have been loaded.)
Note that this is just about what the compilers are permitted to do. In practice, they are liable to be more conservative, not least because these optimizations tend to be relatively expensive to perform.
I don't know how the JIT compiler's optimizers work, an obvious approach would be like this;
- deduce that
getX()
is a method that doesn't require a virtual method dispatch, and therefore a candidate for inlining
- inline the method body into the call at both points
- perform a local data flow analysis which shows that the same variable is loaded twice in the space of a few instructions
- on the basis of that, eliminate the second load.
So in fact, the second call could be entirely optimized away with explicitly reasoning about the method's possible side-effects.