Microsoft announced an experimental feature, still under development, which improves the performance of escape analysis by increasing the opportunities for scalar replacement. Currently the feature is only available for Microsoft Build of OpenJDK, but might become part of OpenJDK in the future.
The feature may be enabled, on Microsoft Build of OpenJDK 11 and 17, by supplying the following JVM flags:
-XX:+UnlockExperimentalVMOptions -XX:+ReduceAllocationMerges
Escape analysis is performed by the Java runtime, or more specifically the JIT compiler, in order to detect objects which are used exclusively inside a method. Normally, each object is created on the heap, but if the object doesn't escape the method, as it's not returned by the method and not used by other methods, then the JVM could use something similar to automatic stack allocation of the object instead of allocating it on the heap. This means the memory is freed automatically after the method with the stack allocated object returns and the garbage collector doesn't have to manage the object.
The HotSpot VM source code file, escape.hpp
, describes how each object is treated:
typedef enum {
UnknownEscape = 0,
NoEscape = 1, // An object does not escape method or thread and it is
// not passed to call. It could be replaced with scalar.
ArgEscape = 2, // An object does not escape method or thread but it is
// passed as argument to call or referenced by argument
// and it does not escape during call.
GlobalEscape = 3 // An object escapes the method or thread.
}
The NoEscape
option is used when the object can be replaced with a scalar. This form of elimination is called scalar replacement. The fields are extracted from the object and stored as some form of local variables inside the method which allocates the object. Afterwards the JIT compiler can store the object fields and local variables inside CPU registers or, if necessary, on the stack.
The experimental feature improves scalar replacement of objects by simplifying object allocation merges. Microsoft Build of JDK 11 contains a simplified version, whereas JDK 17 is capable of performing more scalar replacements.
Microsoft saw an increase of scalar replacements, when enabling the feature on JDK 17, between three and eight percent and a two percent throughput increase with internal benchmarks. Enabling the feature on JDK 11 resulted in a decrease of eight percent for the average P99 latency in memory intensive applications.
Cesar Soares, developer at Microsoft, created a pull request with the implementation on the OpenJDK GitHub repository. The feature is described, discussed and reviewed in the GitHub conversation. The specific changes for JDK 11 and JDK 17 may be viewed as a Git diff
on GitHub.