什么是逃逸分析

文章目录

    • [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 内存模型中的"堆"和"栈"的区分是否已经比较清晰了?这直接关系到为什么"栈上分配"会比"堆"快那么多。

相关推荐
无限的鲜花3 小时前
反射(原创推荐)
java·开发语言
IT二叔4 小时前
Java项目部署-03-teamcity-cicd-docker镜像流水线方式部署
java·ci/cd·持续部署
一路向北he4 小时前
字节钢铁军团--“提供情境,而非控制”
java·开发语言·前端
超级数据查看器4 小时前
超级数据查看器 v10.0 发布
java·大数据·数据库·sqlite·安卓
折哥的程序人生 · 物流技术专研5 小时前
《Java 100 天进阶之路》第50篇:阻塞队列与并发容器(2026版)
java·面试题·java进阶·blockingqueue·并发容器·集合源码·java100天进阶
ai_coder_ai6 小时前
编写自动化脚本,在自己后端服务中使用Open Api进行设备相关操作
java·运维·自动化
硕风和炜6 小时前
【LeetCode: 2492. 两个城市间路径的最小分数 + DFS】
java·算法·leetcode·深度优先·dfs·bfs·并查集
格子软件6 小时前
2026年GEO贴牌代理:分布式多级分账状态机源码深度解构
java·vue.js·分布式·vue·geo
我是一颗柠檬7 小时前
【Java项目技术亮点】加权轮询负载均衡算法
java·算法·负载均衡
灯厂码农7 小时前
C语言动态内存分配完全指南(malloc、calloc、realloc、free)
java·c语言·算法