什么是逃逸分析?
更新时间:2026-09-18。问题驱动短答页。逃逸分析是 JIT 在运行期做的一种优化分析,常和「栈上分配」一起被问到,但这里有个需要澄清的细节。
一句话回答
逃逸分析(Escape Analysis)由 JIT 编译器分析一个对象的作用域有没有逃出方法或线程:如果对象只在方法内使用、不会被外部引用(未逃逸),JVM 就能对它做标量替换(等价于栈上分配,随方法结束自动回收,减轻 GC)和同步消除(去掉不可能被争用的锁)。
三种逃逸程度
| 程度 | 含义 | 能否优化 |
|---|---|---|
| 全局逃逸 | 对象被静态字段、返回值传出方法,被其他线程可见 | 不能 |
| 方法逃逸 | 对象作为返回值/参数传出了当前方法 | 基本不能 |
| 未逃逸(方法内/线程内) | 只在方法内部使用,外部拿不到引用 | 可栈上分配/标量替换、锁消除 |
对象逃逸的典型途径:作为返回值返回、赋值给静态/堆中字段、传给可能保存它的方法。
它带来的三个优化
- 标量替换(Scalar Replacement):把一个未逃逸对象拆散成若干基本类型字段直接放栈上,干脆不创建堆对象。java
public int calc() { Point p = new Point(1, 2); // 未逃逸 return p.x + p.y; // JIT 可直接变成 1+2,连对象都不 new } - (等价的)栈上分配:对象不进堆、随栈帧弹出自动销毁,不产生 GC 压力。
- 同步消除(锁消除):分析出对象不会被其他线程访问,就去掉其上无意义的
synchronized(如方法内私有的StringBuffer追加)。
重要澄清:HotSpot 其实不做「真正的栈上分配」
很多教程说「未逃逸对象直接分配在栈上」,严格说 HotSpot 并没有实现把对象整体放到栈帧的优化,它真正做的是标量替换——只有能被拆成标量(独立字段)的对象才享受「不进堆」的待遇;无法拆的未逃逸对象仍在堆上。面试和文档里说的「栈上分配」,在 HotSpot 语境基本等价指标量替换这个效果。
相关开关(JDK 8 默认都开):
bash
-XX:+DoEscapeAnalysis # 开启逃逸分析(默认开)
-XX:+EliminateAllocations # 标量替换(默认开)
-XX:+EliminateLocks # 锁消除(默认开)实际意义与坑
- 写小而不可变、作用域局部的对象几乎无成本:现代 JIT 会优化掉,不必为了「避免 new」而手工对象池(对象池反而影响逃逸分析和缓存局部性)。
- 别指望逃逸分析消除大对象:无法被标量替换、或被外部引用的对象照样进堆。
- 它是 JIT(C2)优化,解释执行阶段不生效,微基准测试要预热,否则测不出效果。
- 「栈上分配」别讲成 HotSpot 真把对象放进栈,准确表述是「通过标量替换避免堆分配」。
对象「正常情况下」在 Eden 分配、如何晋升老年代见 JVM 运行时内存与堆分代。
总结
逃逸分析是 JIT 在运行期判断对象是否逃出方法或线程:未逃逸的局部对象可被标量替换(拆成基本字段、不创建堆对象,即常说的栈上分配效果,随栈帧回收减轻 GC)并做同步消除(去掉不可能被争用的锁);要注意 HotSpot 并没有真正的整体栈上分配,落地优化靠标量替换,且只对能拆分的小对象、在 JIT 预热后生效,因此现代 Java 里放心使用作用域局部的小对象、不必再手工对象池。