Escape analysis/allocation on the stack/scalar replacement/synchronization elimination

Escape analysis

Preface

we know:

  • Some basic types of variable data (int/short/long/byte/float/double/Boolean/char) and object references are stored in the stack.
  • Objects are mainly stored in the heap, that is, objects created by the new keyword.

However, there is a description of Java heap memory in "In-depth Understanding of the Java Virtual Machine":

  • With the development of the escaping JIT compiler analysis technology matures, 栈上分配, 标量替换优化技术will lead to some subtle changes, all objects are allocated on the heap has gradually become less "absolutely" the.

During compilation, JIT will do many optimizations to the code. Part of the purpose of optimization is 减少内存堆分配压力that one of the important technologies is called 逃逸分析.

逃逸分析(Escape Analysis)It is the most advanced optimization technology in the current Java virtual machine. This is an effective way to reduce the Java program 同步负载and 内存堆分配压力the 跨函数全局数据流analysis algorithm. Through escape analysis, the Java Hotspot compiler can analyze it 一个新对象的引用的使用范围从而决定是否要将这个对象分配到堆上.

The basic behavior of escape analysis is analysis 对象动态作用域: when an object is defined in a method, it may be referenced by an external method, for example, as a call parameter passed to other places, called 方法逃逸.

For example the following 方法返回值逃逸:

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

StringBuffer sbIt is an internal variable of a method. The above code will sbreturn directly . Then this StringBuffermay be changed by other methods, so that its scope is not only inside the method. Although it is a local variable, it is said to escape to the outside of the method. It may even be accessed by external threads, such as assigning to class variables or instance variables that can be accessed in other threads, called 线程逃逸.

If you want the above code StringBuffer sbnot to escape the method, you can write it like this:

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

Do not return directly StringBuffer, then StringBufferthe method will not escape.

Escape analysis includes:

  • Global variable assignment escape
  • Method return value escape
  • Instance reference escape
  • Thread escape: Assignment to class variables or instance variables that can be accessed in other threads

as follows:

public class EscapeAnalysis {
    
    
 
     public static Object object;
     
     public void globalVariableEscape(){
    
       //全局变量赋值逃逸  
         object =new Object();  
      }  
     
     public Object methodEscape(){
    
        //方法返回值逃逸
         return new Object();
     }
     
     public void instancePassEscape(){
    
       //实例引用发生逃逸
        this.speak(this);
     }
     
     public void speak(EscapeAnalysis escapeAnalysis){
    
    
         System.out.println("Escape Hello");
     }
}

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

  1. Synchronization is omitted . 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. Separate objects or scalar replacements . 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 Java code is running, you can specify whether to enable escape analysis through JVM parameters:

  • -XX:+DoEscapeAnalysis : Indicates to open escape analysis

  • -XX:-DoEscapeAnalysis : Indicates to close escape analysis

Since JDK 1.7, escape analysis has been enabled by default. If you need to disable it, you need to specify-XX:-DoEscapeAnalysis

Below, I will mainly introduce the second purpose of escape analysis: converting heap allocation into stack allocation

Allocation on the stack

Through JVM memory allocation, we can know that the objects in JAVA are allocated on the heap. When the object is not referenced, we need to rely on the GC to reclaim the memory. If the number of objects is large, it will bring greater pressure to the GC , Also indirectly affects the performance of the application.

为了减少临时对象在堆内分配的数量,JVM通过逃逸分析确定如果该对象不会被外部访问, Then 标量替换allocate memory on the stack for the object, so that the memory space occupied by the object can be destroyed when the stack frame is popped out of the stack, which reduces the pressure of garbage collection.

Scalar substitution

Scalar (scalar) and aggregate (aggregate)

标量That is, the amount that cannot be further decomposed, and the basic data type of JAVA is a scalar (such as: int, long and other basic data types and reference types, etc.).

The opposite of a scalar is a quantity that can be further decomposed, and this quantity is called 聚合量. In JAVA, an object is an aggregate amount that can be further decomposed.

Scalar replacement process

By 逃逸分析determining that the object will not be accessed externally and the object can be further decomposed, the JVM will not create the object, but will 成员变量decompose the object into several member variables used by this method instead. These substituted member variables allocate space on 栈帧or 寄存器.

If you disassemble a Java object and restore its member variables to scattered variables, this is called 标量替换. The disassembled variables can be analyzed and optimized separately, and space can be allocated on the activity record (stack frame or register) separately, and the original object does not need to allocate space as a whole.

Sync elimination

Synchronization elimination is an optimization technology provided by the Java virtual machine. Through escape analysis, you can determine whether an object will be accessed by other threads.

If there is a method in a class 同步锁, but only one thread is accessing it at 机器码runtime, the synchronization lock will be removed after the escape analysis at this time , which is 线程逃逸the situation that did not occur . Then there will be no resource competition in the reading and writing of the object, and the synchronization lock on the object can be eliminated.

By -XX:+EliminateLocksenabling synchronization elimination, test execution efficiency

public static void main(String[] args) {
    
    
    long start = System.currentTimeMillis();
    EscapeAnalysis escapeAnalysis = new EscapeAnalysis();
    for (int i = 0; i < 1000000; i++) {
    
    
      	escapeAnalysis.createString("Escape", "Hello");
    }
    long bufferCost = System.currentTimeMillis() - start;
    System.out.println("craeteString: " + bufferCost + " ms");
}

public String createString(String ... values){
    
    
    StringBuffer stringBuffer = new StringBuffer(); 
    for (String str : values) {
    
    
      	stringBuffer.append(str + " ");
    }
    return stringBuffer.toString();
}   
-server -XX:+DoEscapeAnalysis -XX:-EliminateLocks
craeteString: 202 ms
 
-server -XX:+DoEscapeAnalysis -XX:+EliminateLocks
craeteString: 173 ms

It can be seen from the test results that if synchronization cancellation is enabled, the execution efficiency of enabling synchronization cancellation is higher than that of not enabling synchronization cancellation.

Guess you like

Origin blog.csdn.net/weixin_44471490/article/details/111502696