逃逸分析是 JIT 编译器做的静态分析:判断一个对象的引用是否会「逃逸」出方法或线程。不逃逸(NoEscape,对象只在方法内部使用)、方法逃逸(作为返回值或存入字段传出)、线程逃逸(被其他线程可见)三个级别。对不逃逸的对象,C2 可以做三类优化:1)标量替换——对象不真正分配,把它的字段拆成独立的局部变量(标量),全程在寄存器/栈帧里,连对象头都不存在;2)栈上分配——广义说法,HotSpot 实际做的是标量替换而非完整对象栈分配;3)锁消除——对象不可能被其他线程访问,其上的同步(如局部 StringBuffer 的 append)直接去掉。
认知纠偏是重点:「Java 对象都在堆上、逃逸分析会把对象放栈上」是流传的简化误读——HotSpot 的产物是标量替换后的一堆局部变量,而不是栈帧上的完整对象;而且优化只发生在代码被 JIT 编译之后,解释执行阶段照常堆分配。相关优化还有 TLAB(线程本地分配缓冲,让堆分配本身近似指针碰撞的廉价操作)。验证手段:-XX:+DoEscapeAnalysis(默认开)、-XX:+EliminateAllocations、用 JMH 对比开关前后的性能、-XX:+PrintEscapeAnalysis(debug 版 JVM)。追问方向:逃逸分析对 StringBuilder 拼接的实际效果、partial escape(部分逃逸,某些分支不逃逸)的处理、Graal 编译器更激进的逃逸分析。
(约 470 字)