什么是逃逸分析

文章目录

    • [1. 对象的三个"逃逸"等级](#1. 对象的三个“逃逸”等级)
    • [2. 逃逸分析后能做什么?(三大神技)](#2. 逃逸分析后能做什么?(三大神技))
      • [① 栈上分配 (Stack Allocation) ------ 最核心的优化](#① 栈上分配 (Stack Allocation) —— 最核心的优化)
      • [② 标量替换 (Scalar Replacement)](#② 标量替换 (Scalar Replacement))
      • [③ 同步消除 (Lock Elision)](#③ 同步消除 (Lock Elision))
    • [3. 它是如何判断对象"逃逸"的?](#3. 它是如何判断对象“逃逸”的?)
    • [4. 为什么要强调它是 JIT 的功能?](#4. 为什么要强调它是 JIT 的功能?)
    • 总结

逃逸分析的核心目的就是判断有没有多线程竞争,如果没有多线程竞争,就会删除锁,提高性能

它的核心任务是:分析一个在方法内创建的对象,它的生命周期是否会超出这个方法的范围(即"逃出"当前方法的控制)。


1. 对象的三个"逃逸"等级

JVM 会根据对象的使用范围,将其标记为三个等级:

  1. 从不逃逸 (No Escape):对象只在当前方法内使用,方法结束,对象就没用了。
  2. 方法逃逸 (Arg Escape):对象作为参数传递给了其他方法,或者被赋值给了其他线程能看到的变量。
  3. 线程逃逸 (Global Escape):对象被赋值给了类变量(static)或者存入了堆中,其他线程可以访问它。

2. 逃逸分析后能做什么?(三大神技)

一旦分析出对象"没有逃逸",JVM 就可以大胆地进行以下"骚操作"来提升性能:

① 栈上分配 (Stack Allocation) ------ 最核心的优化

  • 传统逻辑:Java 几乎所有的对象都在**堆(Heap)**上分配。堆是共享的,创建对象需要加锁找空间,回收对象需要 GC(垃圾回收),压力很大。
  • 优化逻辑:如果对象不逃逸,JVM 就直接在**栈(Stack)**上分配。
  • 结果 :方法一执行完,栈帧弹出,对象直接随之销毁。完全不需要 GC 介入,效率极高。

② 标量替换 (Scalar Replacement)

  • 概念 :有些对象可能不需要作为一个完整的"块"存在。比如一个 Point 对象只有 int xint y
  • 优化逻辑 :如果不逃逸,JVM 会把这个对象拆解掉,直接在栈上创建两个基本类型的变量 xy
  • 结果:连对象头(Object Header)的内存开销都省了,直接变成了寄存器或栈上的基本运算。

③ 同步消除 (Lock Elision)

  • 这就是你之前提到的:如果对象不逃逸,说明只有当前线程能看到它,那它身上的 synchronized 锁就是摆设,JIT 会直接把锁删掉。

3. 它是如何判断对象"逃逸"的?

让我们看一段直观的代码:

java 复制代码
public class EscapeTest {
    public static Object globalVariable;

    // 场景 1:发生逃逸(Global Escape)
    public void globalEscape() {
        Object obj = new Object();
        globalVariable = obj; // 对象被全局变量引用,逃逸了
    }

    // 场景 2:发生逃逸(Arg Escape)
    public Object methodEscape() {
        Object obj = new Object();
        return obj; // 对象被返回给了调用者,逃逸了
    }

    // 场景 3:没有逃逸(No Escape)
    public void noEscape() {
        Object obj = new Object(); // 仅在方法内使用
        System.out.println(obj.hashCode());
    }
}

4. 为什么要强调它是 JIT 的功能?

逃逸分析是一项耗时的静态分析。

  • 如果 JVM 在启动时对每一行逻辑都做分析,Java 启动会慢得像蜗牛。
  • 因此,逃逸分析通常只发生在热点代码(经常被调用的代码)上,由 C2 编译器在运行时动态进行。

总结

逃逸分析就像是一个"侦探",它观察对象的动向:

  • 如果发现对象是"宅男"(不出方法区),就给它安排栈上分配标量替换,省去 GC 麻烦。
  • 如果发现对象是"社交达人"(会逃到别处),那就老老实实去里待着。

这是 Java 能够在中大规模高并发场景下,依然保持极致性能的重要原因之一。

你现在对 Java 内存模型中的"堆"和"栈"的区分是否已经比较清晰了?这直接关系到为什么"栈上分配"会比"堆"快那么多。

相关推荐
Ricky_Theseus2 小时前
C++右值引用
java·开发语言·c++
Rick19932 小时前
Java内存参数解析
java·开发语言·jvm
我是大猴子3 小时前
Spring代理类为何依赖注入失效?
java·后端·spring
勿忘,瞬间3 小时前
多线程之进阶修炼
java·开发语言
014-code3 小时前
线程池参数怎么配才不翻车
java
吴梓穆3 小时前
UE5 c++ 常用方法
java·c++·ue5
王夏奇3 小时前
python中的__all__ 具体用法
java·前端·python
明湖起风了3 小时前
mqtt消费堆积
java·jvm·windows
Free Tester4 小时前
如何判断 LeakCanary 报告的严重程度
java·jvm·算法