Skip to content

面试速答(先看这里)

**一句话结论:**如我们新建的staticObject就是全局逃逸的。

60秒标准回答:

逃逸分析是Java HotSpot Server编译器中JIT优化的一个重要步骤。它在Java SE 6u23及以后的版本中默认启用

对象基于逃逸分析可以有三种状态:全局逃逸(GlobalEscape)、参数逃逸(ArgEscape)和无逃逸(NoEscape)

如我们新建的staticObject就是全局逃逸的。以及下面的方法中的sb对象,也是全局逃逸的

**答题顺序:**结论 → 原理/机制 → 关键流程 → 场景与取舍 → 易错点

回答主线:

  • **要点1:**逃逸分析是Java HotSpot Server编译器中JIT优化的一个重要步骤。
  • **要点2:**在Java中,不同的逃逸状态影响JIT(即时编译器)的优化策略:
  • **要点3:**总的来说,JIT编译器根据对象的逃逸状态采用不同的优化策略,以提高Java程序的性能和效率。
  • **要点4:**关于逃逸分析的论文在1999年就已经发表了,但直到JDK 1.6才有实现,而且这项技术到如今也并不是十分成熟的。
  • **要点5:**其根本原因就是无法保证逃逸分析的性能消耗一定能高于他的消耗。

**记忆锚点:**JIT → Java → DoEscapeAnalysis → GlobalEscape → ArgEscape → NoEscape

关键取舍:

  • 在Java中,不同的逃逸状态影响JIT(即时编译器)的优化策略: 全局逃逸(GlobalEscape) :由于对象可能被多个线程访问,全局逃逸的对象一般不适合进行栈上分配或其他内存优化。
  • 这些优化可以显著提高性能,减少垃圾收集的压力。
  • 总的来说,JIT编译器根据对象的逃逸状态采用不同的优化策略,以提高Java程序的性能和效率。

加分表达:

  • 逃逸分析是Java HotSpot Server编译器中JIT优化的一个重要步骤。
  • 但JIT可能会进行其他类型的优化,如方法内联或循环优化。

追问准备:

  • 围绕「JIT」:底层原理是什么?使用时有哪些边界和常见坑?
  • 围绕「Java」:底层原理是什么?使用时有哪些边界和常见坑?
  • 围绕「DoEscapeAnalysis」:底层原理是什么?使用时有哪些边界和常见坑?
  • 如果线上出现异常,你会如何定位、验证并规避?

典型回答 ​

逃逸分析是Java HotSpot Server编译器中JIT优化的一个重要步骤。它在Java SE 6u23及以后的版本中默认启用。

📄 ✅简单介绍一下JIT优化技术?

打开文档:✅简单介绍一下JIT优化技术?

对象基于逃逸分析可以有三种状态:全局逃逸(GlobalEscape)、参数逃逸(ArgEscape)和无逃逸(NoEscape)。

  1. 全局逃逸(GlobalEscape):对象超出了方法或线程的范围,比如被存储在静态字段或作为方法的返回值。
java
public class GlobalEscapeExample {
    private static Object staticObject;

    public void globalEscape() {
        staticObject = new Object(); // 这个对象赋值给静态字段,因此它是全局逃逸的
    }
}

public static StringBuffer craeteStringBuffer(String s1, String s2) {
    StringBuffer sb = new StringBuffer();
    sb.append(s1);
    sb.append(s2);
    return sb;
}

如我们新建的staticObject就是全局逃逸的。以及下面的方法中的sb对象,也是全局逃逸的。

  1. 参数逃逸(ArgEscape):对象被作为参数传递或被参数引用,但在方法调用期间不会全局逃逸。
java
public class ArgEscapeExample {
    public void methodA() {
        Object localObject = new Object();
        methodB(localObject); // localObject作为参数传递,但不会从methodB中逃逸
    }

    public void methodB(Object param) {
        // 在这里使用param
    }
}

如传递到methodB中的param对象,就是发生了参数逃逸的。因为他从methodA中逃逸到了methodB中。

  1. 无逃逸(NoEscape):对象可以被标量替换,意味着它的内存分配可以从生成的代码中移除。
java
public static String createStringBuffer(String s1, String s2) {
    StringBuffer sb = new StringBuffer();
    sb.append(s1);
    sb.append(s2);
    return sb.toString();
}

如上面的sb,就没有发生逃逸,因为这个对象本身没有作为参数传递,也没有被当做方法返回值,并没有赋值给静态变量。

在Java中,不同的逃逸状态影响JIT(即时编译器)的优化策略:

  1. 全局逃逸(GlobalEscape):由于对象可能被多个线程访问,全局逃逸的对象一般不适合进行栈上分配或其他内存优化。但JIT可能会进行其他类型的优化,如方法内联或循环优化。

📄 ✅简单介绍一下JIT优化技术?

  1. 参数逃逸(ArgEscape):这种情况下,对象虽然作为参数传递,但不会被方法外部的代码使用。JIT可以对这些对象进行一些优化,例如锁消除
  2. 无逃逸(NoEscape):这是最适合优化的情况。JIT可以采取多种优化措施,如在栈上分配内存,消除锁,甚至完全消除对象分配(标量替换)。这些优化可以显著提高性能,减少垃圾收集的压力。
优化手段全局逃逸参数逃逸无逃逸
方法内联YYY
循环优化YYY
锁消除NYY
栈上分配NNY

总的来说,JIT编译器根据对象的逃逸状态采用不同的优化策略,以提高Java程序的性能和效率。

扩展知识 ​

开启逃逸分析 ​

在Java代码运行时,通过JVM参数可指定是否开启逃逸分析,

-XX:+DoEscapeAnalysis: 表示开启逃逸分析

-XX:-DoEscapeAnalysis : 表示关闭逃逸分析 从jdk 1.7开始已经默认开始逃逸分析,如需关闭,需要指定-XX:-DoEscapeAnalysis

逃逸分析并不成熟 ​

关于逃逸分析的论文在1999年就已经发表了,但直到JDK 1.6才有实现,而且这项技术到如今也并不是十分成熟的。

其根本原因就是无法保证逃逸分析的性能消耗一定能高于他的消耗。虽然经过逃逸分析可以做标量替换、栈上分配、和锁消除。但是逃逸分析自身也是需要进行一系列复杂的分析的,这其实也是一个相对耗时的过程。

一个极端的例子,就是经过逃逸分析之后,发现没有一个对象是不逃逸的。那这个逃逸分析的过程就白白浪费掉了。

虽然这项技术并不十分成熟,但是他也是即时编译器优化技术中一个十分重要的手段。