Here's the example I tried to reproduce from Java Performance: The Definitive Guide, Page 97 on the topic of Escape Analysis. This is probably what should happen:
getSum()must get hot enough and with appropriate JVM parameters it must be inlined into the callermain().- As both
listandsumvariables do not escape frommain()method they could be marked asNoEscapehence JVM could use stack-allocation for them instead of heap-allocation.
But I ran it through jitwatch and the result showed that getSum() compiles into native-assembly and doesn't get inlined into main(). Not to mention consequently stack-allocation didn't happen either.
What am I doing wrong in here ? (I have put the whole code and hotspot log here.)
Here's the code:
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.stream.IntStream;
public class EscapeAnalysisTest {
private static class Sum {
private BigInteger sum;
private int n;
Sum(int n) {
this.n = n;
}
synchronized final BigInteger getSum() {
if (sum == null) {
sum = BigInteger.ZERO;
for (int i = 0; i < n; i++) {
sum = sum.add(BigInteger.valueOf(i));
}
}
return sum;
}
}
public static void main(String[] args) {
ArrayList<BigInteger> list = new ArrayList<>();
for (int i = 1; i < 1000; i++) {
Sum sum = new Sum(i);
list.add(sum.getSum());
}
System.out.println(list.get(list.size() - 1));
}
}
JVM parameters I used:
-server
-verbose:gc
-XX:+UnlockDiagnosticVMOptions
-XX:+TraceClassLoading
-XX:MaxInlineSize=60
-XX:+PrintAssembly
-XX:+LogCompilation
In order to know why something is inlined or not, you can look in the compilation log for the
inline_successandinline_failtags.However to even get something inlined, the caller would have to be compiled, in your case you want an inline in the
mainmethod so the only way this is going to happen is on-stack replacement (OSR). Looking at your log, you can see a few OSR compilations but none of themainmethod: there is simply not enough work in yourmainmethod.You can fix that by increasing the number of iteration of your
forloop. By increasing it to100_000, I got a first OSR compilation.For such a small example is looked at
-XX:+PrintCompilation -XX:+PrintInliningrather than the wholeLogCompilationoutput and I saw:That's not very helpful... but looking a bit a HotSpot source code reveals that it's probably because of a policy that prevents C1 compilations to inline methods that have been OSR compiled by C2. In any case, looking at the inlining done by the C1 compilation is not that interesting.
Adding more loop iterations (
1_000_000with a modulo on the argument toSumto reduce run time) gets us a C2 OSR ofmainwith:That part of C2's policy is rather self-descriptive and is controlled by the
InlineSmallCodeflag:-XX:InlineSmallCode=4ktells HotSpot that the threshold for "big method" is at 4kB of native code. On my machine that was enough to getgetSuminlined:(Note that i never had to use
MaxInlineSize)For reference here's the modified loop: