Java escape analysis technology

Escape Analysis

What is escape?

Escaping refers to an object created within a method, in addition to being referenced within the method body, it is also referenced by other variables outside the method body; the consequence of this is that after the method is executed, the The object created in the method cannot be recycled by the GC because it is referenced by other variables. In a normal method call, the object created in the method body will be recycled after the execution is completed; therefore, because it cannot be recycled, it becomes an escape.

In the Java compilation system, the process of turning a Java source code file into a computer executable machine instruction requires two stages of compilation:

         The first paragraph: Convert the .java file into a .class file.

         The second paragraph: Compilation is the process of converting .class into machine instructions.

The first compilation is the javac command

In the second compilation stage, JVM interprets bytecodes to translate them into corresponding machine instructions, reads in one by one, and interprets the translation one by one. Obviously, after interpretation and execution, its execution speed will inevitably be much slower than executable binary bytecode programs. This is the function of the traditional JVM Interpreter. In order to solve this efficiency problem, JIT (Just-in-Time Compilation) technology was introduced.

After the introduction of JIT technology, Java programs are still interpreted and executed through an interpreter. When the JVM finds that a method or code block is running particularly frequently, it will consider it as a "hot spot code" (Hot Spot Code). Then JIT will translate part of the "hot code" into machine code related to the local machine, and optimize it, and then cache the translated machine code for next use.

Because of the content of JIT compilation and hot spot detection, I have already introduced it in the in-depth analysis of Java compilation principles, so I won’t repeat them here. This article mainly introduces the optimization in JIT. The most important one in JIT optimization is escape analysis.

Escape analysis

Regarding the concept of escape analysis, you can refer to the article that objects are not necessarily all allocated on the heap. Here is a brief review:

The basic behavior of escape analysis is to analyze the dynamic scope of an object : when an object is defined in a method, it may be referenced by an external method, such as being passed to other places as a call parameter, which is called method escape.

For example, the following code:

public static StringBuffer craeteStringBuffer(String s1, String s2) {
    StringBuffer sb = new StringBuffer();
    sb.append(s1);
    sb.append(s2);
    return sb;
}
public static String createStringBuffer(String s1, String s2) {
    StringBuffer sb = new StringBuffer();
    sb.append(s1);
    sb.append(s2);
    return sb.toString();
}

The sb in the first piece of code escapes, but the sb in the second piece of code does not escape.

Using escape analysis, the compiler can optimize the code as follows:

1. Synchronous omission.

If an object is found to be accessible only from one thread, then the operation of this object can be ignored.

2. Convert heap allocation into stack allocation.

If an object is allocated in a subroutine, so that the pointer to the object will never escape, the object may be a candidate for stack allocation instead of heap allocation.

3. Separation of objects or scalar replacement.

Some objects may not need to exist as a continuous memory structure to be accessed, so part (or all) of the object may not be stored in the memory, but stored in the CPU register.

When the Java code is running, you can specify whether to enable escape analysis through the JVM parameter,

-XX:+DoEscapeAnalysis: Indicates to open escape analysis

-XX:-DoEscapeAnalysis: Indicates that the escape analysis has been turned off by default since jdk 1.7. If you need to turn it off, you need to specify -XX:-DoEscapeAnalysis

Four, synchronization omission

When dynamically compiling the synchronization block, the JIT compiler can use escape analysis to determine whether the lock object used by the synchronization block can only be accessed by one thread and not released to other threads.

If the lock object used by the synchronization block can only be accessed by one thread through this analysis, the JIT compiler will cancel the synchronization of this part of the code when compiling the synchronization block. This process of canceling synchronization is called synchronization omission, or lock elimination .

Such as the following code:

public void f() {
    Object hollis = new Object();
    synchronized(hollis) {
        System.out.println(hollis);
    }
}

The hollis object is locked in the code, but the life cycle of the hollis object is only in the f() method and will not be accessed by other threads, so it will be optimized during the JIT compilation phase. Optimized into:

public void f() {
    Object hollis = new Object();
    System.out.println(hollis);
}

Therefore, when using synchronized, if JIT finds that there is no thread safety problem after escape analysis, it will do lock elimination.

Scalar substitution

Scalar refers to data that cannot be broken down into smaller data. The primitive data type in Java is a scalar. In contrast, the data that can be decomposed is called Aggregate. Objects in Java are aggregates, because they can be decomposed into other aggregates and scalars.

In the JIT stage, if it is found that an object cannot be accessed by the outside world through escape analysis, then after JIT optimization, the object will be disassembled into several member variables contained therein instead. This process is scalar replacement.

public static void main(String[] args) {
   alloc();
}
private static void alloc() {
   Point point = new Point(1,2);
   System.out.println("point.x="+point.x+"; point.y="+point.y);
}
class Point{
    private int x;
    private int y;
}

In the above code, the point object does not escape the alloc method, and the point object can be disassembled into scalars. Then, JIT will not directly create a Point object, but directly use two scalar int x and int y to replace the Point object.

The above code, after scalar replacement, will become:

private static void alloc() {
   int x = 1;
   int y = 2;
   System.out.println("point.x="+x+"; point.y="+y);
}

It can be seen that after the escape analysis of Point, the aggregation amount was found that he did not escape, it was replaced with two aggregation amounts. So what are the benefits of scalar substitution?

The advantage of scalar replacement is that it can greatly reduce the heap memory usage. Because once there is no need to create an object, there is no need to allocate heap memory anymore.

Scalar substitution provides a good basis for allocation on the stack.

Allocation on the stack

In the Java virtual machine, objects are allocated memory in the Java heap, which is a common sense. However, there is a special case, that is, if it is found after escape analysis that an object has no escape method, then it may be optimized to be allocated on the stack. In this way, there is no need to allocate memory on the heap, and there is no need for garbage collection.

For a detailed introduction to the allocation on the stack, you can refer to the object is not necessarily all allocated memory on the heap

Here, I still want to say briefly, in fact, in existing virtual machines, there is no real implementation of stack allocation. In our case, the objects are not allocated on the heap. In our example, the objects are not necessarily all allocated on the heap. Allocation is actually implemented by scalar replacement.

Escape analysis is immature

The paper on escape analysis was published in 1999, but it was not implemented until JDK 1.6, and the technology is not very mature until now.

The fundamental reason is that there is no guarantee that the performance consumption of escape analysis will be higher than his consumption. Although after escape analysis, scalar replacement, stack allocation, and lock elimination can be done. However, escape analysis itself also requires a series of complex analysis, which is actually a relatively time-consuming process.

An extreme example is that after escape analysis, it is found that no object does not escape. Then the process of escape analysis is wasted.

Although this technology is not very mature, it is also a very important means of just-in-time compiler optimization technology.

Guess you like

Origin blog.csdn.net/qq_30264689/article/details/102911269