Not so far ago I had been profiling an application and noticed an interesting optimization that applies to the compiled byte code.
Here is my program:
public class InstanceOnly {
public static void main(final String[] args) throws InterruptedException {
int sum = 0;
for (int i = 0; i < Integer.MAX_VALUE; ++i) {
Book b = new Discounted(new Prime(i));
sum += b.price();
Thread.sleep(10);
}
System.out.printf("Total: %d\n", sum);
}
interface Book {
int price();
}
static class Discounted implements Book {
private final Book book;
Discounted(final Book b) {
this.book = b;
}
@Override
public int price() {
return this.book.price() / 2;
}
}
static class Prime implements Book {
private final int usd;
Prime(final int u) {
this.usd = u;
}
@Override
public int price() {
return this.usd * 3;
}
}
}
As you might notice it is rather simple - we just use decorator pattern here in order to calculate the total price of all books. The application will run very long time, so I have enough time to profile it. I’m using YourKit java profiling which use bytecode instrumentation in order to get precise number of invocations of the each method. I profile the application during one minute and get the next results:
"verification.instances.InstanceOnly.main(String[]) InstanceOnly.java","1"
"java.lang.Thread.sleep(long) Thread.java (native)", "5307"
"verification.instances.InstanceOnly$Prime.<init>(int) InstanceOnly.java", "5307"
"verification.instances.InstanceOnly$Discounted.price() InstanceOnly.java","5307"
"verification.instances.InstanceOnly$Discounted.<init>(InstanceOnly$Book) InstanceOnly.java","5307"
But where is the verification.instances.InstanceOnly$Prime.price()
call? I can see that we only invoke the Prime
constructor. I initially suspected that it might be a problem with JIT
optimizations, where the method gets inlined. To address this, I disabled all optimizations by using the -Djava.compiler=NONE
Java parameter. However, even with this configuration, the targeted method is still missing from the profiling results
I also attempted to add a dummy statement like System.out.println
:
@Override
public int price() {
System.out.println("Dummy println");
return this.usd * 3;
}
And only in this case did I finally observe the desired method:
InstanceOnly$Prime.price()
So, I'm completely confused because I can't understand who is actually performing the optimization and how I can disable it to see the InstanceOnly$Prime.price()
in the profiling results without adding dummy statements. Does anyone know what this could be?