Q37 of 40 · Core Java
What is escape analysis and stack allocation in the JVM?
Core JavaSeniorescape-analysisjitjvmperformancestack-allocationgc
Short answer
Short answer: Escape analysis is a JIT-compiler optimisation that determines whether an object created inside a method is accessible (escapes) outside that method's stack frame. If the object does not escape — i.e., it's not returned, stored in a field, or passed to another thread — the JIT can allocate it on the stack instead of the heap, or even replace it with scalar variables. Stack-allocated objects are freed when the frame pops, with zero GC pressure.
Detail
Three escape states
- NoEscape — object stays local to one method: stack allocation or scalar replacement possible.
- ArgEscape — object passed as argument to a method that doesn't let it escape further: synchronisation on it can be eliminated.
- GlobalEscape — object reachable from a static field or returned/stored beyond the current method: must be heap-allocated normally.
// Example 1: loop-local point — likely stack-allocated by JIT
record Point(double x, double y) {}
void computeCentroid(double[] xs, double[] ys) {
double sumX = 0, sumY = 0;
for (int i = 0; i < xs.length; i++) {
// Point does not escape this loop body (not stored anywhere)
var p = new Point(xs[i], ys[i]); // JIT may scalar-replace this
sumX += p.x();
sumY += p.y();
}
}
import java.util.ArrayList;
import java.util.List;
// Example 2: object escapes — heap allocation required
List<Point> points = new ArrayList<>();
void processAll(double[] xs, double[] ys) {
for (int i = 0; i < xs.length; i++) {
var p = new Point(xs[i], ys[i]);
points.add(p); // p escapes into the list — heap allocated
}
}
Lock elision (bonus optimisation enabled by escape analysis)
// StringBuffer is synchronized. If sb doesn't escape, JIT removes all locks.
String buildKey(String prefix, int id) {
var sb = new StringBuffer(); // local, doesn't escape
sb.append(prefix).append(id); // JIT elides synchronisation entirely
return sb.toString(); // string result escapes, not sb
}
Practical impact for QA automation
- Short-lived helper objects (request builders, response wrappers) used only within a test method may be stack-allocated, reducing GC pauses in high-frequency test loops.
- Avoid storing test helpers in instance fields unless necessary — it forces heap allocation and increases GC pressure.
- Enabled by default since Java 6u23 (
-XX:+DoEscapeAnalysis).
// WHAT INTERVIEWERS LOOK FOR
Understanding that the JIT can move heap allocations to the stack when objects don't escape. The three escape categories. Lock elision as a bonus optimisation. Strong answers mention scalar replacement (replacing an object with its component primitives) and JMH as the correct way to measure allocation impact.
// COMMON PITFALL
Assuming all `new` allocations go to the heap. Since Java 6, the JIT routinely eliminates heap allocations for non-escaping objects. Micro-benchmarks that allocate aggressively and expect GC pressure may see less than expected if escape analysis kicks in.