How are JVM optimizations based on assumptions?

Nguyen Tanh :

In section 12.3.3., "Unrealistic Sampling of Code Paths" the Java Concurrency In Practice book says:

In some cases, the JVM may make optimizations based on assumptions that may only be true temporarily, and later back them out by invalidating the compiled code if they become untrue

I cannot understand above statement.

  1. What are these JVM assumptions?
  2. How does the JVM know whether the assumptions are true or untrue?
  3. If the assumptions are untrue, does it influence the correctnes of my data?
Marco13 :

The statement that you quoted has a footnote which gives an example:

For example, the JVM can use monomorphic call transformation to convert a virtual method call to a direct method call if no classes currently loaded override that method, but it invalidates the compiled code if a class is subsequently loaded that overrides the method.

The details are very, very, very complex here. So the following is a extremely oversimpilified example.

Imagine you have an interface:

 interface Adder { int add(int x); }

The method is supposed to add a value to x, and return the result. Now imagine that there is a program that uses an implementation of this class:

class OneAdder implements Adder { 
    int add(int x) {
        return x+1;
    }
}

class Example {

    void run() {
        OneAdder a1 = new OneAdder();
        int result = compute(a1);
        System.out.println(result);
    }

    private int compute(Adder a) {
        int sum = 0;
        for (int i=0; i<100; i++) {
            sum = a.add(sum);
        }
        return sum;
    }
}

In this example, the JVM could do certain optimizations. A very low-level one is that it could avoid using a vtable for calling the add method, because there is only one implementation of this method in the given program. But it could even go further, and inline this only method, so that the compute method essentially becomes this:

private int compute(Adder a) {
    int sum = 0;
    for (int i=0; i<100; i++) {
        sum += 1;
    }
    return sum;
}

and in principle, even this

private int compute(Adder a) {
    return 100;
}

But the JVM can also load classes at runtime. So there may be a case where this optimization has already been done, and later, the JVM loads a class like this:

class TwoAdder implements Adder { 
    int add(int x) {
        return x+2;
    }
}

Now, the optimization that has been done to the compute method may become "invalid", because it's not clear whether it is called with a OneAdder or a TwoAdder. In this case, the optimization has to be undone.

This should answer 1. of your question.

Regarding 2.: The JVM keeps track of all the optimizations that have been done, of course. It knows that it has inlined the add method based on the assumption that there is only one implementation of this method. When it finds another implementation of this method, it has to undo the optimization.

Regarding 3.: The optimizations are done when the assumptions are true. When they become untrue, the optimization is undone. So this does not affect the correctness of your program.


Update:

Again, the example above was very simplified, referring to the footnote that was given in the book. For further information about the optimization techniques of the JVM, you may refer to https://wiki.openjdk.java.net/display/HotSpot/PerformanceTechniques . Specifically, the speculative (profile-based) techniques can probably be considered to be mostly based on "assumptions" - namely, on assumptions that are made based on the profiling data that has been collected so far.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=327429&siteId=1