I was teaching an introductory programming course today and was walking through some simple code involving variable assignments in Java. The point of the code wasn't to show off anything particular exciting, but mostly to make sure students understood variable assignment statements.
I had the following method up on the board and was tracing through it one line at a time:
private void simpleMethod() {
int myInt = 137;
myInt = 42;
myInt = myInt + 1;
/* ... code using myInt ... */
}
A student asked me whether myInt
would ever actually hold the values 137 and 42 when the program ran, or if it would just jump straight to holding 43. I told the student that the code would execute each line in turn, so the variable would actually hold these intermediate values.
Honestly, though, I wasn't sure what bytecode javac
would emit (completely ignoring the optimizations done by the JVM). Is javac
(or any Java compiler) legally allowed to optimize the silly assignment statements away and to instead just directly initialize myInt
to 43?
According to javap
, on my system, the above code compiled with javac
produces
0: sipush 137
3: istore_1
4: bipush 42
6: istore_1
7: iload_1
8: iconst_1
9: iadd
10: istore_1
11: return
So there is no optimization going on here. My question, though, is whether it's legal to optimize this or not, so this doesn't resolve anything.
The JVMs JIT compiler is one of the fascinating mechanisms on the Java platform. It optimizes your code for performance, without giving away its readability. Not only that, beyond the “static” optimization methods of inlining, it also makes decisions based on the way that the code performs in practice.
Compiler optimization is generally implemented using a sequence of optimizing transformations, algorithms which take a program and transform it to produce a semantically equivalent output program that uses fewer resources or executes faster.
The JIT compiler helps improve the performance of Java programs by compiling bytecodes into native machine code at run time. The JIT compiler is enabled by default.
The JIT compiler is enabled by default, and is activated when a Java method is called. The JIT compiler compiles the bytecodes of that method into native machine code, compiling it "just in time" to run. When a method has been compiled, the JVM calls the compiled code of that method directly instead of interpreting it.
The JLS specifies only the contract of observable behavior that your program produces. Since myInt
is local, the optimization can indeed be optimized at compile time, since this would produce a behavior consistent with the spec, and there's nothing in the spec that says it's not allowed (at least, not that I found!). Chapter 1 of the spec specifies the observable-ness of the spec explicitly: This document fully specifies the (apparent) order of evaluation of expressions...
. Since the apparent behavior is unchanged by constant-folding to myInt = 43
, the optimization would be consistent with the JLS.
In fact, the compilation target of a Java application isn't even specified in the JLS. Chapter 1 says that Java applications "normally" compile to the bytecode specified in the JVM spec (a separate document), but it does not require that they do so. There are some statements that must be optimized at compile time, but myInt
is not one such. Even if myInt
were a field, I think the optimization would be allowed; the different behavior would still be valid behavior, even if myInt
is volatile (since it represents one valid ordering of events).
So, short answer, I think your student is correct; it's perfectly fine to optimize it to just myInt = 43
. That said, javac
generally does very little -- virtually nothing -- in the way of optimization. Optimizations are pretty much all done in the JIT.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With