You can search the wechat official account [Jet and Programming] for more exciting articles

The original release in his own blog platform “www.jetchen.cn/EscapeAnaly…”


In Java, the creation of objects is usually allocated by the heap memory for storage. When the heap memory is insufficient, the GC will garbage collect the heap memory. If the GC runs too many times, it will affect the performance of the program. Its purpose is to determine which objects can be stored in stack memory rather than heap memory, thereby reducing the frequency of GC occurrences, which is one of the most common JVM optimization techniques.


What is escape analysis

“Are objects in Java allocated in heap memory?” — “Not really”

As mentioned briefly earlier, if objects are allocated in heap memory, the increasing number of objects will inevitably involve frequent GC runs, so escape analysis was born to mitigate this situation.

Escape Analysis is simply a technique where the Java Hotspot VIRTUAL machine can analyze the scope of use of newly created objects and decide whether or not to allocate memory on the Java heap.

After an object is created in a method, if the object is referenced elsewhere than in the method body, the GC may not be able to reclaim it immediately if the method completes execution because the object is referenced. This is a memory escape.

Escape is A verb. For example, if A escapes from B, then A refers to the object created in the method and B refers to the method body, which can be simply interpreted as the object escaping from the method body.

State of the escape

An object can have three escape states:

  1. GlobalEscape: when an object’s scope escapes from the current method or thread,

There are several scenarios: ① the object is a static variable, ② the object is an escaped object, and ③ the object is the return value of the current method

  1. Parameter escape (ArgEscape) : An object is passed as a method argument or referenced by an argument, but no global escape occurs during the call. This state is determined by the bytecode of the called method.
  2. No escape: That is, the object in the method does not escape.
public class EscapeAnalysisTest {

    public static Object globalVariableObject;

    public Object instanceObject;

    public void globalVariableEscape(a){
        globalVariableObject = new Object();  // Static variable, visible to external threads, escape occurs
    }

    public void instanceObjectEscape(a){
        instanceObject = new Object();  // Assign to the instance field in the heap, visible to external threads, escape occurs
    }
    
    public Object returnObjectEscape(a){
        return new Object();   // Return instance, external thread visible, escape occurs
    }

    public void noEscape(a){
        Object noEscape = new Object();   // Create thread only visible, object no escape}}Copy the code

Advantages of escape analysis

  • Open escape analysis:-XX:+DoEscapeAnalysis
  • Closure escape analysis:-XX:-DoEscapeAnalysis
  • Display analysis results:-XX:+PrintEscapeAnalysis

The function of escape analysis is to screen out the objects without escape and optimize them in the following three aspects:

Sync elimination (lock elimination)

Because synchronous locking is very costly to performance, the compiler removes the synchronization lock on an object when it determines that it has not escaped.

This is enabled by default in JDK1.8, but on the basis that escape analysis is already enabled.

  • Open lock elimination:-XX:+EliminateLocks
  • Close lock elimination:-XX:-EliminateLocks

Scalar replacement

First understand that scalars and aggregate quantities, references to base types and objects can be understood as scalars and cannot be further decomposed. The quantities that can be further decomposed are aggregate quantities, such as objects.

An object is an aggregate quantity, which can be further decomposed into scalars and its member variables into discrete variables. This is called scalar substitution.

In this way, if an object does not escape, it does not need to be created at all, and only the scalar members used by it are created on the stack or register, saving memory and improving application performance.

Scalar substitution is also enabled by default in JDK1.8, but also based on the fact that escape analysis is enabled.

  • Enable scalar substitution:-XX:+EliminateAllocations
  • To turn off scalar substitution:-XX:-EliminateAllocations
  • Display scalar replacement details:-XX:+PrintEliminateAllocations

Stack allocation

Stack memory allocation is well understood, as mentioned above, which means that objects allocated to heap memory are allocated to stack memory instead of heap memory, thus reducing the heap memory footprint and thus reducing the frequency of GC.

Escape analysis test

The code looks like this: for loop 100 million times, inside the loop calls the external allot() method, and the allot() method simply creates an object, but that object is internal, so it doesn’t escape, so theoretically the JVM will optimize it, we’ll see. And we will compare the running time of each program after the escape analysis is turned on and off:

/ * * *@ClassName: EscapeAnalysisTest
 * @Description: http://www.jetchen.cn Escape analysis Demo *@Author: Jet.Chen
 * @Date: 2020/11/23 14:26
 * @Version: 1.0 * * /
public class EscapeAnalysisTest {

    public static void main(String[] args) {
        long t1 = System.currentTimeMillis();
        for (int i = 0; i < 100000000; i++) {
            allot();
        }
        long t2 = System.currentTimeMillis();
        System.out.println(t2-t1);
    }

    private static void allot(a) {
        Jet jet = new Jet();
    }

    static class Jet {
        publicString name; }}Copy the code

Here is the code for our escape analysis test, with a thread pause at the end of the mian() method to see how much memory is in the JVM at this point.

Step 1: Test open escape

Since the environment is JDK1.8, escape analysis is enabled by default, so run directly, the results are as follows, the program takes 3 ms:

At this point, the thread is asleep, and we look at the memory and see that there are 110,000 new Jet objects in the heap.

Step 2: Test shutdown escape

We turn off the escape analysis and run it again (using java-xx: -doEscapeAnalysis EscapeAnalysisTest to run the code). We get the following result, which takes 400 milliseconds:

When we look at memory, we see that there are over 30 million new Jet objects in the heap.

So, whether in terms of the execution time of the code (3ms VS 400ms) or the number of objects in the heap memory (110,000 VS 30 million), there is a positive benefit to enabling escape analysis in the above scenarios.

Step 3: Test scalar substitutions

Let’s test on and off scalar substitution as shown below:

It can be seen from the figure above that, in the above extreme scenarios, turning on and off scalar replacement has a huge impact on performance. In addition, it also verifies that the prerequisite for scalar replacement to take effect is that escape analysis has been enabled, otherwise it is meaningless.

Test lock elimination

To test lock elimination, we need to simply adjust the code to lock the contents of the allot() method as follows:

private static void allot(a) {
    Jet jet = new Jet();
    synchronized (jet) {
        jet.name = "jet Chen"; }}Copy the code

Then we run the test code, and the results are clear. In the above scenario, the impact of opening and closing lock elimination on application performance is also significant.

conclusion

The principles of escape analysis are simple to understand, but there are a number of factors that need to be considered when the JVM is in practice.

For example, escape analysis cannot be done at static compile time and must be done in the JIT. The reason: conflicts with Java’s dynamic nature. Because you can change the behavior of a class at run time through dynamic proxies, escape analysis cannot tell that the class has changed. The bottom line: Because only when enough runtime data has been collected can the JVM better determine whether an object has escaped. (Please refer to www.zhihu.com/ques….)

Of course, escape analysis is not without its disadvantages, because escape analysis requires a certain amount of performance to perform analysis, so if the objects in the method are all in the escape state, then there is no optimization effect, thus losing this part of the performance cost.