Skip to content

介绍

逃逸分析(Escape Analysis)是 JVM 在即时编译(JIT)阶段执行的一种优化分析手段,核心目的是:分析一个对象的引用范围,判断这个对象是否会"逃出"当前方法 / 线程的作用域。

  • 如果对象的引用只在当前方法内有效,没有被外部(方法外、其他线程)引用,就称为没有逃逸
  • 如果对象的引用被传出当前方法(比如返回给调用者、赋值给全局变量、传递给其他线程),就称为发生逃逸

简单比喻:

  • 没有逃逸:你在自己房间里用的杯子,只用一次,用完就扔,不会被拿到房间外;
  • 发生逃逸:你把这个杯子拿给了房间外的人,或者放在客厅(所有人都能拿到),杯子的"生命周期"就超出了你的房间。

逃逸分析的三种逃逸场景

不逃逸(No Escape)

对象仅在当前方法内创建和使用,引用从未离开方法。

java
public void test() {
    // User对象仅在test()方法内使用,无任何外部引用 → 不逃逸
    User user = new User("张三", 20);
    System.out.println(user.getName());
    // 方法执行完,user引用失效,对象可被直接回收
}

方法逃逸(Method Escape)

对象的引用被传出当前方法(比如作为返回值、赋值给方法外的变量)。

java
// 场景1:作为返回值返回 → 方法逃逸
public User createUser() {
    User user = new User("李四", 25);
    return user; // user引用返回给调用者,逃出了createUser()方法
}

// 场景2:赋值给全局变量 → 方法逃逸
private User globalUser;
public void setGlobalUser() {
    User user = new User("王五", 30);
    globalUser = user; // user引用赋值给全局变量,逃出当前方法
}

线程逃逸(Thread Escape)

对象的引用被传递到其他线程(比如作为线程任务的参数)。

java
public void testThreadEscape() {
    User user = new User("赵六", 35);
    // user引用传递给新线程,逃出当前线程 → 线程逃逸
    new Thread(() -> System.out.println(user.getName())).start();
}

逃逸分析的核心作用(JVM 优化手段)

JVM 做逃逸分析的最终目的是优化程序性能,主要有 3 个优化方向:

栈上分配(Stack Allocation)

这是最核心的优化:

  • 对于不逃逸的对象,JVM 不会把它分配到堆上,而是直接分配在当前方法的栈帧中;
  • 方法执行完毕后,栈帧出栈,对象随栈空间一起释放,无需 GC(垃圾回收)处理,大幅减少堆 GC 的压力。

标量替换(Scalar Replacement)

  • 标量:无法再分解的基本类型(int、long、byte 等);
  • 聚合量:可以分解的对象(比如 User 对象包含 name、age 两个标量);
  • 优化逻辑:如果对象不逃逸,JVM 会直接将对象"拆解"成多个标量(局部变量)存储在栈帧中,无需创建完整对象,进一步节省内存。

示例(标量替换效果):

java
public void testScalar() {
    // 原代码:创建User对象(不逃逸)
    User user = new User("钱七", 40);
    System.out.println(user.getName() + "," + user.getAge());

    // JVM标量替换后,等价于直接创建两个局部变量,无需创建User对象:
    String name = "钱七";
    int age = 40;
    System.out.println(name + "," + age);
}

同步消除(Lock Elision)

如果一个对象不逃逸(仅当前线程访问),那么对这个对象的同步锁(比如 synchronized)就没有意义,JVM 会自动消除这个锁,减少锁竞争的开销。

java
public void testLockElision() {
    // StringBuffer是线程安全的(有synchronized),但sb对象仅在方法内使用,不逃逸
    StringBuffer sb = new StringBuffer();
    sb.append("a").append("b"); // JVM会消除sb的同步锁,等价于使用非线程安全的StringBuilder
}

逃逸分析的注意事项

  • 默认开启:JDK 1.7 及以上版本,HotSpot JVM 默认开启逃逸分析(无需手动配置);
  • 编译期优化:逃逸分析是 JIT 编译阶段的优化,不是类加载或运行时的实时分析;
  • 不是万能的:逃逸分析有一定的开销,JVM 会权衡优化收益,仅对"小对象、短生命周期"的不逃逸对象做栈上分配,大对象仍会分配到堆上。

总结

  • 核心逻辑:逃逸分析是 JVM 分析对象引用范围的手段,判断对象是否"逃出"当前方法 / 线程;
  • 核心价值:为栈上分配、标量替换、同步消除提供依据,减少堆 GC 和锁开销;
  • 关键区分:只有"不逃逸"的对象才会被 JVM 优化到栈上,发生逃逸的对象仍分配在堆上。

上次更新于: