I wonder if JVM/javac is smart enough to turn
// This line...
string a = foo();
string foo()
{
return bar();
}
string bar()
{
return some-complicated-string computation;
}
into
string a = bar();
Or strip unnecessary call to foo() in release case (because unreachable code):
string a = foo(bar());
// bar is the same
...
string foo(string b)
{
if (debug) do-something-with(b);
}
My feeling is yes for the first example and “not so sure” for the second one, but could anyone give me some pointers/links to confirm that?
javacwill present bytecode that is a faithful representation of the original Java program that generated the bytecode (except in certain situations when it can optimize: constant folding and dead-code elimination). However, optimization may be performed by the JVM when it uses the JIT compiler.For the first scenario it looks like the JVM supports inlining (see under Methods here and see here for an inlining example on the JVM).
I couldn’t find any examples of method inlining being performed by
javacitself. I tried compiling a few sample programs (similar to the one you have described in your question) and none of them seemed to directly inline the method even when it wasfinal. It would seem that these kind of optimizations are done by the JVM’s JIT compiler and not byjavac. The “compiler” mentioned under Methods here seems to be the HotSpot JVM’s JIT compiler and notjavac.From what I can see,
javacsupports dead-code elimination (see the example for the second case) and constant folding. In constant folding, the compiler will precalculate constant expressions and use the calculated value instead of performing the calculation during runtime. For example:compiles to the following bytecode:
Note that the bytecode has an
sipush 300instead ofaload‘sgetfields and aniadd.300is the calculated value. This is also the case forprivate finalvariables. Ifaandbwere not static, the resulting bytecode will be:Here also, an
sipush 300is used.For the second case (dead-code elimination), I used the following test program:
which gives the following bytecode:
As you can see, the
foois not called at all inbazbecause the code inside theifblock is effectively “dead”.Sun’s (now Oracle’s) HotSpot JVM combines interpretation of the bytecode as well as JIT compilation. When bytecode is presented to the JVM the code is initially interpreted, but the JVM will monitor the bytecode and pick out parts that are frequently executed. It coverts these parts into native code so that they will run faster. For piece of bytecode that are not used so frequently, this compilation is not done. This is just as well because compilation has some overhead. So it’s really a question of tradeoff. If you decide to compile all bytecode to nativecode, then the code can have a very long start-up delay.
In addition to monitoring the bytecode, the JVM can also perform static analysis of the bytecode as it is interpreting and loading it to perform further optimization.
If you want to know the specific kinds of optimizations that the JVM performs, this page at Oracle is pretty helpful. It describes the performance techniques used in the HotSpot JVM.