首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么逃逸分析在JDK17中不起作用?

为什么逃逸分析在JDK17中不起作用?
EN

Stack Overflow用户
提问于 2021-12-04 06:57:43
回答 2查看 281关注 0票数 0

打开escape analysis时,GC仍然每毫秒发生一次,运行时间与没有优化的时间相同。

我通过使用JMH进行测试得出了这个结论,下面是我的代码和测试结果。

代码:

代码语言:javascript
复制
/**
 * @author Jmc
 */
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 3, time = 10)
@Measurement(iterations = 3, time = 10)
@Fork(1)
public class EscapeAnalysisTest {
    /**
     * VM Options: -Xlog:gc:empty.gc.log -Xmx30M
     */
    @Benchmark
    public void empty() {
        // do nothing
    }

    /**
     * VM Options: -Xlog:gc:withEA.gc.log -Xmx30M
     */
    @Benchmark
    public void withEA() {
        Object o = null;
        for (int i = 0; i < 1_000_000_000; i++) {
            o = new Object();
        }
        int hashcode = o.hashCode();
    }

    /**
     * VM Options: -Xlog:gc:withoutEA.gc.log -Xmx30M -XX:-DoEscapeAnalysis
     */
    @Benchmark
    public void withoutEA() {
        Object o = null;
        for (int i = 0; i < 1_000_000_000; i++) {
            o = new Object();
        }
        int hashcode = o.hashCode();
    }
}

输出:

  • 空- JMH

代码语言:javascript
复制
# JMH version: 1.32
# VM version: JDK 17, Java HotSpot(TM) 64-Bit Server VM, 17+35-LTS-2724
# VM invoker: D:\Programs\JDK\jdk-17\bin\java.exe
# VM options: -Xlog:gc:empty.gc.log -Xmx30M --enable-preview -javaagent:D:\Programs\JetBrains\IntelliJ IDEA 2021.2.2\lib\idea_rt.jar=54629:D:\Programs\JetBrains\IntelliJ IDEA 2021.2.2\bin -Dfile.encoding=UTF-8
# Blackhole mode: full + dont-inline hint
# Warmup: 3 iterations, 10 s each
# Measurement: 3 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Average time, time/op
# Benchmark: com.test.jmh.EscapeAnalysisTest.empty

...

Benchmark                 Mode  Cnt  Score   Error  Units
EscapeAnalysisTest.empty  avgt    3  0.517 ± 1.005  ns/op

  • withEA - JMH

代码语言:javascript
复制
# JMH version: 1.32
# VM version: JDK 17, Java HotSpot(TM) 64-Bit Server VM, 17+35-LTS-2724
# VM invoker: D:\Programs\JDK\jdk-17\bin\java.exe
# VM options: -Xlog:gc:withEA.gc.log -Xmx30M --enable-preview -javaagent:D:\Programs\JetBrains\IntelliJ IDEA 2021.2.2\lib\idea_rt.jar=54648:D:\Programs\JetBrains\IntelliJ IDEA 2021.2.2\bin -Dfile.encoding=UTF-8
# Blackhole mode: full + dont-inline hint
# Warmup: 3 iterations, 10 s each
# Measurement: 3 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Average time, time/op
# Benchmark: com.test.jmh.EscapeAnalysisTest.withEA

...

Benchmark                  Mode  Cnt          Score           Error  Units
EscapeAnalysisTest.withEA  avgt    3  900223198.990 ± 468305893.304  ns/op

  • withoutEA - JMH

代码语言:javascript
复制
# JMH version: 1.32
# VM version: JDK 17, Java HotSpot(TM) 64-Bit Server VM, 17+35-LTS-2724
# VM invoker: D:\Programs\JDK\jdk-17\bin\java.exe
# VM options: -Xlog:gc:withoutEA.gc.log -Xmx30M -XX:-DoEscapeAnalysis --enable-preview -javaagent:D:\Programs\JetBrains\IntelliJ IDEA 2021.2.2\lib\idea_rt.jar=54708:D:\Programs\JetBrains\IntelliJ IDEA 2021.2.2\bin -Dfile.encoding=UTF-8
# Blackhole mode: full + dont-inline hint
# Warmup: 3 iterations, 10 s each
# Measurement: 3 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Average time, time/op
# Benchmark: com.test.jmh.EscapeAnalysisTest.withoutEA

...

Benchmark                     Mode  Cnt          Score           Error  Units
EscapeAnalysisTest.withoutEA  avgt    3  905957459.624 ± 472436917.469  ns/op

  • 空GC日志

代码语言:javascript
复制
[0.039s][info][gc] Using G1
[0.010s][info][gc] Using G1

  • withEA GC日志

代码语言:javascript
复制
[0.011s][info][gc] Using G1
[0.011s][info][gc] Using G1
[0.596s][info][gc] GC(0) Pause Young (Normal) (G1 Evacuation Pause) 13M->2M(30M) 5.488ms
[0.603s][info][gc] GC(1) Pause Young (Normal) (G1 Evacuation Pause) 14M->2M(30M) 3.900ms
[0.606s][info][gc] GC(2) Pause Young (Normal) (G1 Evacuation Pause) 15M->2M(30M) 0.403ms

...

[63.402s][info][gc] GC(14235) Pause Young (Normal) (G1 Evacuation Pause) 20M->2M(30M) 0.519ms
[63.406s][info][gc] GC(14236) Pause Young (Normal) (G1 Evacuation Pause) 20M->2M(30M) 0.558ms
[63.410s][info][gc] GC(14237) Pause Young (Normal) (G1 Evacuation Pause) 20M->2M(30M) 0.509ms
[63.414s][info][gc] GC(14238) Pause Young (Normal) (G1 Evacuation Pause) 20M->2M(30M) 0.650ms
[63.418s][info][gc] GC(14239) Pause Young (Normal) (G1 Evacuation Pause) 20M->2M(30M) 0.832ms

  • withoutEA GC日志

代码语言:javascript
复制
[0.013s][info][gc] Using G1
[0.012s][info][gc] Using G1
[0.607s][info][gc] GC(0) Pause Young (Normal) (G1 Evacuation Pause) 13M->2M(30M) 4.942ms
[0.614s][info][gc] GC(1) Pause Young (Normal) (G1 Evacuation Pause) 14M->2M(30M) 3.855ms
[0.617s][info][gc] GC(2) Pause Young (Normal) (G1 Evacuation Pause) 15M->2M(30M) 0.345ms

...

[63.837s][info][gc] GC(15086) Pause Young (Normal) (G1 Evacuation Pause) 20M->2M(30M) 0.971ms
[63.841s][info][gc] GC(15087) Pause Young (Normal) (G1 Evacuation Pause) 20M->2M(30M) 1.038ms
[63.845s][info][gc] GC(15088) Pause Young (Normal) (G1 Evacuation Pause) 20M->2M(30M) 0.953ms
[63.850s][info][gc] GC(15089) Pause Young (Normal) (G1 Evacuation Pause) 20M->2M(30M) 1.233ms
[63.858s][info][gc] GC(15090) Pause Young (Normal) (G1 Evacuation Pause) 20M->2M(30M) 0.499ms

注意:从上面的代码和结果中,您可以看到空方法没有发生gc,对于另外两个java方法,运行时间大致相同。当打开EA时仍然会发生GC,我想知道原因。

EN

回答 2

Stack Overflow用户

发布于 2021-12-07 17:46:12

逃逸分析很脆弱。在您的例子中,循环后的语句int hashcode = o.hashCode();足以使其无效。调用非重写的hashCode()可以防止对象被消除,因为它是一个身份敏感的操作。但是,通过使用循环之后在循环体中创建的对象,您进一步达到了分析器的限制。

为了简化对是否存在逃逸分析的测试(或者更广泛地说,存在任何消除分配的东西),并防止干扰死代码消除,我使用了

代码语言:javascript
复制
public class TestEA {
    public static void main(String[] args) {
        int result;
        if(args.length == 0 || !args[0].equals("NoUseAfterLoop")) {
            result = runEaTest();
        }
        else {
            result = runEaTestNoUseAfterLoop();
        }
        System.out.println(result);
    }
    private static int runEaTest() {
        record MyObject(int i) {};
        int sum = 0;
        MyObject o = null;
        for (int i = 1; i < 1_000_000_000; i++) {
            o = new MyObject(1);
            sum += o.i();
        }
        sum += o.i();
        return sum;
    }
    private static int runEaTestNoUseAfterLoop() {
        record MyObject(int i) {};
        int sum = 0;
        MyObject o = null;
        for (int i = 0; i < 1_000_000_000; i++) {
            o = new MyObject(1);
            sum += o.i();
        }
        return sum;
    }
}

此示例使用临时对象存储和检索用于计算返回的和的值。因此,循环不能被平缓移除。

当我运行这个程序时

代码语言:javascript
复制
jdk-17\bin\java -Xmx40m -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC TestEA

我得到了期望

代码语言:javascript
复制
[0.007s][warning][gc,init] Consider enabling -XX:+AlwaysPreTouch to avoid memory commit hiccups
Terminating due to java.lang.OutOfMemoryError: Java heap space

代码语言:javascript
复制
jdk-17\bin\java -Xmx40m -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC TestEA NoUseAfterLoop

没有导致OutOfMemoryError

代码语言:javascript
复制
[0.007s][warning][gc,init] Consider enabling -XX:+AlwaysPreTouch to avoid memory commit hiccups
1000000000

由于在64位HotSpot JVM中创建1,000,000,000对象需要16,000,000,000字节,而且EpsilonGC不意味着要重复使用内存,所以在没有错误的情况下运行具有40M堆的循环表明分配已被消除(并演示了与其他方法的区别)。

这是来自循环体的对象的使用,在这里抑制了逃逸分析。但是这个例子只执行一次方法。行为可能在第一次执行后再次更改,即当所有分支至少被占用一次时。

票数 1
EN

Stack Overflow用户

发布于 2021-12-08 09:22:19

逃逸分析(EA)在某些情况下工作:

在方法中声明的objects.

  • Objects

  • 对象不能向任何其他线程、方法公开,在方法中声明的不能调用本机方法。

  • 方法应该多次调用,然后EA才能工作。

G 211

下面的测试用例将证明EA有效。

代码

代码语言:javascript
复制
/**
 * @author Jmc
 */
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 3, time = 10)
@Measurement(iterations = 3, time = 10)
@Fork(1)
public class EscapeAnalysisTest {
    static class Int {
        private final int value;

        public Int(int value) {
            this.value = value;
        }

        public int hash() {
            return value;
        }
    }

    /**
     * VM Options: -Xlog:gc:withoutEA.gc.log -Xmx30M -XX:-DoEscapeAnalysis
     */
    @Benchmark
    public void withoutEA() {
        Int n = null;
        for (int i = 0; i < 1_000_000_000; i++) {
            n = new Int(i);
        }

        // ensure for loop is executed
        int hash = n.hash();
    }

    /**
     * VM Options: -Xlog:gc:withEA.gc.log -Xmx30M
     */
    @Benchmark
    public void withEA() {
        Int n = null;
        for (int i = 0; i < 1_000_000_000; i++) {
            n = new Int(i);
        }
        
        // ensure for loop is executed
        int hash = n.hash();
    }
}

JMH结果

无EA

代码语言:javascript
复制
# JMH version: 1.32
# VM version: JDK 17, Java HotSpot(TM) 64-Bit Server VM, 17+35-LTS-2724
# VM invoker: D:\Programs\JDK\jdk-17\bin\java.exe
# VM options: -Xlog:gc:withoutEA.gc.log -Xmx30M -XX:-DoEscapeAnalysis --enable-preview -javaagent:D:\Programs\JetBrains\IntelliJ IDEA 2021.2.2\lib\idea_rt.jar=62639:D:\Programs\JetBrains\IntelliJ IDEA 2021.2.2\bin -Dfile.encoding=UTF-8
# Blackhole mode: full + dont-inline hint
# Warmup: 3 iterations, 10 s each
# Measurement: 3 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Average time, time/op
# Benchmark: com.test.jmh.EscapeAnalysisTest.withoutEA

...

Benchmark                     Mode  Cnt           Score           Error  Units
EscapeAnalysisTest.withoutEA  avgt    3  3829083588.889 ± 204309555.709  ns/op

  • 与EA

代码语言:javascript
复制
# JMH version: 1.32
# VM version: JDK 17, Java HotSpot(TM) 64-Bit Server VM, 17+35-LTS-2724
# VM invoker: D:\Programs\JDK\jdk-17\bin\java.exe
# VM options: -Xlog:gc:withEA.gc.log -Xmx30M --enable-preview -javaagent:D:\Programs\JetBrains\IntelliJ IDEA 2021.2.2\lib\idea_rt.jar=62630:D:\Programs\JetBrains\IntelliJ IDEA 2021.2.2\bin -Dfile.encoding=UTF-8
# Blackhole mode: full + dont-inline hint
# Warmup: 3 iterations, 10 s each
# Measurement: 3 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Average time, time/op
# Benchmark: com.test.jmh.EscapeAnalysisTest.withEA

...

Benchmark                  Mode  Cnt  Score   Error  Units
EscapeAnalysisTest.withEA  avgt    3  0.659 ± 0.442  ns/op

GC测井

无EA

代码语言:javascript
复制
[0.013s][info][gc] Using G1
[0.028s][info][gc] Using G1
[0.524s][info][gc] GC(0) Pause Young (Normal) (G1 Evacuation Pause) 13M->2M(30M) 4.725ms
[0.531s][info][gc] GC(1) Pause Young (Normal) (G1 Evacuation Pause) 14M->2M(30M) 3.718ms
[0.534s][info][gc] GC(2) Pause Young (Normal) (G1 Evacuation Pause) 15M->2M(30M) 0.345ms

...

[68.030s][info][gc] GC(15298) Pause Young (Normal) (G1 Evacuation Pause) 20M->2M(30M) 1.189ms
[68.035s][info][gc] GC(15299) Pause Young (Normal) (G1 Evacuation Pause) 20M->2M(30M) 1.272ms
[68.039s][info][gc] GC(15300) Pause Young (Normal) (G1 Evacuation Pause) 20M->2M(30M) 1.132ms
[68.044s][info][gc] GC(15301) Pause Young (Normal) (G1 Evacuation Pause) 20M->2M(30M) 1.044ms
[68.049s][info][gc] GC(15302) Pause Young (Normal) (G1 Evacuation Pause) 20M->2M(30M) 1.184ms

  • 与EA

代码语言:javascript
复制
[0.011s][info][gc] Using G1
[0.009s][info][gc] Using G1
[0.553s][info][gc] GC(0) Pause Young (Normal) (G1 Evacuation Pause) 13M->2M(30M) 5.623ms
[0.559s][info][gc] GC(1) Pause Young (Normal) (G1 Evacuation Pause) 14M->2M(30M) 3.804ms
[0.562s][info][gc] GC(2) Pause Young (Normal) (G1 Evacuation Pause) 15M->2M(30M) 0.309ms

...

[7.185s][info][gc] GC(1695) Pause Young (Normal) (G1 Evacuation Pause) 20M->2M(30M) 0.638ms
[7.189s][info][gc] GC(1696) Pause Young (Normal) (G1 Evacuation Pause) 20M->2M(30M) 0.524ms
[7.193s][info][gc] GC(1697) Pause Young (Normal) (G1 Evacuation Pause) 20M->2M(30M) 0.572ms
[7.196s][info][gc] GC(1698) Pause Young (Normal) (G1 Evacuation Pause) 20M->2M(30M) 0.370ms
[7.200s][info][gc] GC(1699) Pause Young (Normal) (G1 Evacuation Pause) 20M->2M(30M) 0.542ms

从测试结果可以看出:

Without EA

  • After
  1. With EA的运行时间是,比调用 withEA()的次数少得多(因为这个测试是在7.2s之后),EA工作,然后GC不再发生。相反,withoutEA()发生GC整个时间.

由此得出的结论是EA工作并采取了一些措施来减少GC (可能是在堆栈上创建对象,或者一次性销毁堆上的对象),从而使java程序更高效地运行。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70223634

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档