2024-10-26  阅读(46)
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://www.skjava.com/mianshi/baodian/detail/1721227302

回答

逃逸分析(Escape Analysis)是 JVM 的一项优化技术。JVM 通过分析代码中对象的生命周期和作用域,判断某个对象是否会逃逸出某个作用域(如方法或者线程),如果 JVM 可以确定该对象不会逃逸,则可以做一些优化策略,例如栈上分配、标量替换、同步消除,从而提升程序的执行效率,降低内存分配和垃圾回收的负担。

详解

逃逸分析详解

  • 逃逸:如果一个对象在创建后能够在当前作用域之外被访问或引用,就称为这个对象“逃逸”了。逃逸有两种:
    • 方法逃逸:对象被方法外部使用,例如,将对象作为返回值或者传递给其他方法,这种情况下对象必须在堆上分配。
    • 线程逃逸:对象被其他线程访问,例如对象被赋值给一个静态变量或者实例变量,这种情况下也需要在堆上分配。
  • 非逃逸:如果一个对象仅在当前作用域内使用,且不会被外部引用,则该对象为“非逃逸”对象。

对于非逃逸对象,JVM 有三个优化手段:栈上分配、同步消除标量替换

栈上分配

通常情况下,Java 中的对象都是在堆上分配。但是当 JVM 通过逃逸分析确认某个对象不会逃逸出方法的作用域,则会考虑将该对象分配在栈上。

当对象满足以下条件之一,通常会被分配到栈上:

  • 对象没有被返回给方法外部。
  • 对象没有被赋值给类的成员变量或静态变量。
  • 对象没有被其他线程访问。

由于栈属于线程私有,当方法执行完成后,栈帧弹出,栈上分配的对象内存也随之释放。因此,在栈上分配的对象是不需要被垃圾回收器回收,减轻了 GC 的压力。例如:

public class Point {
    int x, y;
    Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

public class EscapeAnalysisTest {
    public static void main(String[] args) throws Exception {
        for (int i = 0 ; i < 1000 ; i++) {
            createPoint();
        }
    }

    public static void createPoint() {
        Point point = new Point(1, 2);
        System.out.println(point.x + ", " + point.y);
    }
}

createPoint() 方法中,point 对象只在方法内使用,且也没有被其他线程使用,则 JVM 可能会将该对象在栈上分配。

标量替换

标量替换的核心思想是将一个对象分解为多个独立的基本类型变量(标量),从而避免为整个对象分配内存。

在计算机科学中,标量(Scalar)是指不能再分解的基本数据类型,例如整数、浮点数等等。还有一个聚合量(Aggregate)则是由多个标量组成的复杂数据类型,如数组、对象等等。

当 JVM 通过逃逸分析确定某个对象不会逃逸出方法范围,且对象可以分解为多个独立的基本类型时,JVM就会考虑进行标量替换。

标量替换通常在以下情况下发生:

  • 对象的字段在方法内部可以独立操作。
  • 对象的字段可以被拆解为局部变量。

举个例子说明下。

public class EscapeAnalysisTest2 {
    public static void main(String[] args) throws Exception {
        int result = calculate();
        System.out.println(result);
    }

    private static int calculate() {
        Point point = new Point(1, 2);
        return point.x + point.y;
    }
}

如果没有进行标量替换,point 对象会在 calculate() 中创建并分配内存(可能是堆或者栈),并在访问 point.xpoint.y 时,通过对象引用来操作这些字段。

但是,通过逃逸分析,JVM 可以确定 point 不会逃逸出 calculate() 且 point 对象是可以被分解为两个 int 基本数据类型,所以 JVM 会进行标量替换。具体来说,JVM会将point对象的字段xy替换为两个独立的局部变量,从而避免创建Point对象。优化代码如下:

public static int calculate() {
    int x = 1;
    int y = 2;
    return x + y;
}

在这种情况下,JVM不会为point对象分配内存,而是直接在栈或寄存器中操作xy这两个标量。

所以,标量替换有两个优势:

  1. 可以避免分配整个对象的内存,从而减少堆或栈上的内存使用,降低 GC 的压力。
  2. 可以直接在寄存器或栈上操作标量,减少对内存的访问次数,从而提升程序的运行效率。

同步消除

同步消除,也叫做锁消除,其主要目标是通过逃逸分析来确定某些锁操作是否有必要。

如果JVM能够确认某个锁对象不会被其他线程访问,那么它可以安全地消除这些不必要的同步操作,从而减少上下文切换、锁竞争等开销,提高程序的执行效率。


Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。

它的内容包括:

  • 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
  • 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
  • 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
  • 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
  • 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
  • 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
  • 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
  • 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw

目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:

想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询

同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。

阅读全文