Wednesday, September 7, 2011

Java Secret: Are static blocks interpreted?

Overview

Are static block interpreted or does the JIT play a part?

When are methods optimised?

A method will be optimised when it is called often enough. This is controlled by the -XX:CompileThreshold=10000 flag. The compilation occurs in the background by default and a short time later, the optimised version of the code will be used. (Which is why it doesn't always happen at exact the 10K mark)

However, loops can also be optimised if they are called often enough. This typically takes about 2-5 seconds. This is why any bound loop which does nothing takes about 2-5 seconds. (Which is how long it takes the JIT to realise the loop doesn't do anything)

How does this apply to static blocks?

static block are never called more than once. Even if they are loaded by different class loaders, they are optimised independently. (In the unlikely event you loaded the same class 10,000 times, it still wouldn't be optimised)

However, it is quite possible to have a loop iterate many times in a static block (though rare) This can result in the static block begin optimised by the JIT.

public class Main {
    static {
        long start = System.nanoTime();
        for (int i = 0; i < 5000; i++) ;
        long time = System.nanoTime() - start;
        System.out.printf("Took %.3f ms to iterate 5 thousand times%n", time / 1e6);

        long start1 = System.nanoTime();
        for (int i = 0; i < 5000; i++) ;
        long time1 = System.nanoTime() - start1;
        System.out.printf("Took %.3f ms to iterate 5 thousand times%n", time1 / 1e6);

        long start2 = System.nanoTime();
        for (int j = 0; j < 1000 * 1000; j++)
            for (int i = 0; i < 1000 * 1000; i++) ;
        long time2 = System.nanoTime() - start2;
        System.out.printf("Took %.3f ms to iterate 1 trillion times%n", time2 / 1e6);

        long start3 = System.nanoTime();
        for (int j = 0; j < 1000 * 1000; j++)
            for (int i = 0; i < 1000 * 1000; i++) ;
        long time3 = System.nanoTime() - start3;
        System.out.printf("Took %.3f ms to iterate 1 trillion times%n", time3 / 1e6);
    }

    public static void main(String[] args) {
    }
}
run the with -XX:+PrintCompilation flag outputs.
64   1       java.lang.String::charAt (33 bytes)
     75   2       java.lang.String::hashCode (64 bytes)
Took 0.067 ms to iterate 5 thousand times
Took 0.062 ms to iterate 5 thousand times
     88   1%      Main:: @ 124 (249 bytes)
Took 4.860 ms to iterate 1 trillion times
Took 0.001 ms to iterate 1 trillion times

You can see that a loop of 5,000 is not enough to trigger optimisation, but a much larger loop does trigger optimisation. Without the JIT, there is no way the loop of one trillion time would finish in 3 seconds.

Once the method has been optimise the last one trillion loop takes practically no time at all.

Note

The client JVM doesn't appear to perform this optimisation and will wait a very long time.

No comments:

Post a Comment