Escape Analysis in Java 6 Update 14 - Some Informal Benchmarks
Join the DZone community and get the full member experience.
Join For FreeSun released update 14 of the Java 6 JDK and JRE. As well as the usual collection of bug fixes, this release includes some experimental new features designed to improve the performance of the JVM (see the release notes). One of these is Escape Analysis.
What Is Escape Analysis?
Escape analysis is a compiler optimization technique that analyzes an object's access path to determine whether or not it is escaped from its current scope. If an object is not escaped, then the compiler can allocate it on the stack rather than on the heap.
This eliminates the need for memory allocation and garbage collection for the object, which can improve performance.
Allocating on Heap Vs the Stack
To understand the implications of allocating an object on the stack or heap, it is first necessary to understand how these two data structures work.
The heap is a pool of memory that is managed by the JVM. When an object is created, it is allocated a space on the heap. The size of this space depends on the object's type and the fields it contains.
The stack is a data structure that is used to store local variables and function call information. When a function is called, its parameters and local variables are stored on the stack. When the function returns, these variables are popped off the stack.
Allocating an object on the heap requires two operations:
The object is allocated a space on the heap.
The object's reference (a pointer to its location on the heap) is stored on the stack.
Allocating an object on the stack requires only one operation:
The object is allocated a space on the stack.
Because allocating an object on the heap requires an extra operation, it is generally slower than allocating on the stack. In addition, heap allocation requires the JVM to manage the memory used by the object. This includes finding a free block of memory of the appropriate size and dealing with fragmentation.
Stack allocation does not require any memory management, as the stack is managed by the operating system.
What Is a Compiler?
A compiler is a program that converts code written in one language (usually called the source language) into another language (usually called the target language). The target language can be either machine code or another high-level programming language.
In Java 6 Update 14, escape analysis was turned off by default but could be enabled by passing the -XX:+DoEscapeAnalysis flag to the java command.
However, now Escape analysis is supported and enabled by default in Java SE 6u23 and later.
What Is An Object?
In Java, an object is a self-contained unit of functionality that can be created and used independently.
A typical object consists of both data and behavior. Data is represented by fields (also called member variables or instance variables), and behavior is represented by methods.
You can think of an object as a "thing" that can perform certain actions and contains certain information. For example, a car is an object that can be driven (behavior) and has a color (data).
Why Is Escape Analysis Important Today?
Escape analysis is important because it can improve performance by eliminating the need for memory allocation and garbage collection for certain objects.
When we say "garbage collection", that refers to the process of reclaiming memory that is no longer being used by the program.
In general, garbage collection can be a CPU-intensive operation, so eliminating it can improve performance. Any time you can avoid allocating memory and/or doing garbage collection, your program will run faster.
What Are Some Examples of Objects That Can Benefit from Escape Analysis?
Small objects that are created and used within a single method can often benefit from escape analysis.
For example, consider the following code:
public static void main(String[] args) {
String s = "Hello, world!";
System.out.println(s);
}
In this code, the String object created on line 3 is not escaped. It is created within the main method and is used only within that method.
As a result, the String object can be allocated on the stack rather than on the heap. This eliminates the need for memory allocation and garbage collection for that object.
How Does Escape Analysis Work?
To determine whether or not an object is escaped, the compiler uses a flow-sensitive, interprocedural analysis. This means that the compiler not only analyzes the code within a single method but also looks at how the object is used in other methods that are called from the current method.
If an object is only used within the scope of a single method and is never passed as an argument to another method, then it is not escaped.
For example, consider the following code:
public class MyClass {
public static void main(String[] args) {
MyClass obj = new MyClass();
obj.method1();
}
public void method1() {
int x = 5;
method2(x);
}
public void method2(int y) {
int z = y * 2;
}
}
In this code, the MyClass object is created in the main method and passed as an argument to the method1 method.
The method1 method then creates a local variable x and passes it as an argument to the method2 method.
The MyClass object is therefore escaped because it is used in another method (method2) that is called from the current method (method1).
In contrast, the local variable x is not escaped, because it is only used within the scope of the method1 method.
When Should I Use Escape Analysis?
Escape analysis can be used to improve the performance of any Java application. It helps to reduce the memory footprint.
It is most effective when objects are short-lived and are only used within a single method.
To see what kind of impact escape analysis might have on my applications, I decided to try it on a couple of my more CPU-intensive Java programs. Escape analysis is turned off by default since it is still experimental. It is enabled using the following command-line option:
-XX:+DoEscapeAnalysis
Benchmark 1
The first program I tested is a statistical simulation. Basically, it generates millions of random numbers (using Uncommons Maths naturally) and does a few calculations.
VM Switches: -server
95 seconds
VM Switches: -server -XX:+DoEscapeAnalysis
73 seconds
Performance improvement using Escape Analysis: 23%
Benchmark 2
The second program I tested is an implementation of non-negative matrix factorisation.
VM Switches: -server
22.6 seconds
VM Switches: -server -XX:+DoEscapeAnalysis
20.8 seconds
Performance improvement using Escape Analysis: 8%
In both cases, escape analysis gave a noticeable performance improvement. I was quite surprised by how much of an improvement I saw in the first benchmark. I wasn't expecting to see anything like that.
Escape analysis is an important tool in the JVM performance tuner's armory and it's good to see that it is being improved and refined in each new release of the JDK.
Are There Other Ways To Reduce Memory Footprint in Java?
Yes, there are other ways to reduce the memory footprint in Java.
One way is to use object pooling. Object pooling is a technique for reusing objects that have already been created.
Another way is to use a generational garbage collector. A generational garbage collector is designed to collect short-lived objects more efficiently than long-lived objects.
Finally, you can use a compacting garbage collector. A compacting garbage collector moves objects around in memory so that there are fewer gaps between objects. This can help to reduce memory fragmentation.
Conclusions
These benchmarks are neither representative nor comprehensive. Nevertheless, for certain types of programs, the addition of escape analysis appears to be another significant step forward in JVM performance.
In conclusion, although escape analysis is still experimental, it is definitely worth experimenting with in your own applications.
If you are interested in learning more about escape analysis, I highly recommend reading about it further on Dzone.
Opinions expressed by DZone contributors are their own.
Comments