“This article has participated in the good article summoning order activity, click to view: back end, big front double track submission, 20,000 yuan prize pool for you to challenge”

I am Chen PI, a Coding ITer in the Internet. Search “Chen PI’s JavaLib” on wechat and read the latest articles at the first time. Reply to [data], you can get my carefully organized technical data, electronic books, interview materials of first-line big factories and excellent resume template.

The introduction

When I interview someone in the process, I almost always ask about the JVM memory model, although some people say asking these is the interview build aircraft carriers, work screw. If you want to be a CRUD coder, you can choose not to know this.

In a Q&A with the JVM memory model, some people can say that objects are allocated on the heap. But when I ask if objects must be stored on the heap, most people say yes, or hesitate.

It actually tells you that the object is allocated on the heap. However, with the development of JIT just-in-time compilers and the maturity of escape analysis techniques, the allocation of all objects to the heap has become less and less absolute. Optimization techniques such as stack allocation, scalar substitution, and lock elimination can be subtle.

As we know, the Java source code we write is compiled by JavAC into a bytecode file, which is then loaded into memory by a classloader, and the JVM reads and interprets the bytecode line by line into the corresponding machine instructions for execution. Obviously, interpretative execution is much slower than that of binary programs that can be executed directly, such as C programs.

So in order to improve efficiency, JIT (Just-in-time compiler) optimization technology is introduced. Java program or can be carried by the interpreter to explain, but if a method or blocks of code to run more frequently, the JVM think it is a hot spot in the code, then hot machine instruction code translation costs, and optimize, cached, next time again run this code, run directly without having to explain.

An important optimization technique in JIT is Escape Analysis.

Escape analysis

Escape analysis is an analysis of whether an object can escape from a method, the dynamic scope of the object. An object is considered to have escaped if it is defined within a method and is likely to be used by external references to the method.

For example, the following Person object has escaped, potentially being referenced externally to the method.

public Person personEscape(a) {
  Person person = new Person();
  return person;
}
Copy the code

So why do escape analysis, in fact, the ultimate purpose is to optimize the program to improve performance. There are the following optimization technology points:

  • On the stack
  • Scalar replacement
  • Lock elimination

When JDK1.7 starts, escape analysis is enabled by default. You can use the following parameters to start or stop escape analysis.

# open
-XX:+DoEscapeAnalysis
# close
-XX:-DoEscapeAnalysis
Copy the code

On the stack

If an object is analyzed without an escape method, it may be allocated to the stack. This eliminates the need for GC collection in the heap, improving performance.

package com.chenpi;

/ * * *@Description
 * @AuthorDried tangerine or orange peel *@Date 2021/7/14
 * @Version1.0 * /
public class EscapeAnalysisTest {

  public static void main(String[] args) {

    long startTime = System.currentTimeMillis();

    for (int i = 0; i < 10000000; i++) {
      stackAlloc();
    }

    System.out.println((System.currentTimeMillis() - startTime) + "ms");
  }

  public static void stackAlloc(a) {
    Person person = new Person("Orange".18); }}class Person {

  private String name;
  private long age;

  public Person(String name, long age) {
    this.name = name;
    this.age = age; }}Copy the code

Set vm parameters to enable escape analysis and print GC logs.

-Xms200m -Xmx200m -XX:+DoEscapeAnalysis -XX:+PrintGC
Copy the code

Run the program as follows, consuming only 10 ms, and no GC.

10ms
Copy the code

Turn off escape analysis and print the GC log.

-Xms200m -Xmx200m -XX:-DoEscapeAnalysis -XX:+PrintGC
Copy the code

The result of running the program is a more than 10-fold increase in elapsed time, accompanied by multiple GCS.

[GC (Allocation Failure)  51712K->784K(196608K), 0.0050396 secs]
[GC (Allocation Failure)  52496K->784K(196608K), 0.0030730 secs]
[GC (Allocation Failure)  52496K->752K(196608K), 0.0013993 secs]
[GC (Allocation Failure)  52464K->720K(196608K), 0.0018371 secs]
176ms
Copy the code

Scalar replacement

  • Scalar: a type that cannot be decomposed into smaller data. For example, a basic data type is a scalar.
  • Aggregates: Data types that can be decomposed into other aggregates or scalars, such as object reference types.

If an object does not escape, the JIT can optimize to decompose the object into several scalars instead. That’s the scalar substitution.

public void scalarReplace(a) {
  Coordinates coordinates = new Coordinates(105.10.80.22);
  System.out.println(coordinates.longitude);
  System.out.println(coordinates.latitude);
}
Copy the code

In the demonstration above, the coordinates object does not escape, so the JIT compiler can optimize using scalar substitution. The final optimization into the following program.

public void scalarReplace(a) {
  System.out.println(105.10);
  System.out.println(80.22);
}
Copy the code

Actually in the existing virtual machine, there is no real implementation of stack allocation, it is actually implemented by scalar substitution.

Lock elimination

Why remove locks? Since locking degrades performance, how best to avoid it? The JIT can optimize to eliminate the lock if it analyzes that the locked object does not escape and can only be accessed by one thread. Also known as synchronous ellipsis.

public void lockRemove(a) {
  synchronized (new Object()) {
    System.out.println("I am tangerine peel!); }}Copy the code

In the above demo, the Object does not escape, so it is only accessible by the current thread, so the JIT compiler can optimize lock removal. The final optimization into the following program.

public void lockRemove(a) {
  System.out.println("I am tangerine peel!);
}
Copy the code

conclusion

However, with the development of JIT just-in-time compilers and the maturity of escape analysis techniques, the allocation of all objects to the heap has become less and less absolute. With escape analysis, objects can be allocated on the stack, reducing GC and improving program performance.

But does a program with escape analysis on perform better than one without? Not necessarily. The escape analysis technology is also very complex, so it is also a time-consuming process. If all objects are found to escape after the escape analysis, it cannot be optimized. Then the escape analysis process consumes time and does not play an optimization role, so the gain is not worth the loss.