打开escape analysis时,GC仍然每毫秒发生一次,运行时间与没有优化的时间相同。
我通过使用JMH进行测试得出了这个结论,下面是我的代码和测试结果。
代码:
/**
* @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 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# 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# 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[0.039s][info][gc] Using G1
[0.010s][info][gc] Using G1[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[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,我想知道原因。
发布于 2021-12-07 17:46:12
逃逸分析很脆弱。在您的例子中,循环后的语句int hashcode = o.hashCode();足以使其无效。调用非重写的hashCode()可以防止对象被消除,因为它是一个身份敏感的操作。但是,通过使用循环之后在循环体中创建的对象,您进一步达到了分析器的限制。
为了简化对是否存在逃逸分析的测试(或者更广泛地说,存在任何消除分配的东西),并防止干扰死代码消除,我使用了
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;
}
}此示例使用临时对象存储和检索用于计算返回的和的值。因此,循环不能被平缓移除。
当我运行这个程序时
jdk-17\bin\java -Xmx40m -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC TestEA我得到了期望
[0.007s][warning][gc,init] Consider enabling -XX:+AlwaysPreTouch to avoid memory commit hiccups
Terminating due to java.lang.OutOfMemoryError: Java heap space而
jdk-17\bin\java -Xmx40m -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC TestEA NoUseAfterLoop没有导致OutOfMemoryError
[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堆的循环表明分配已被消除(并演示了与其他方法的区别)。
这是来自循环体的对象的使用,在这里抑制了逃逸分析。但是这个例子只执行一次方法。行为可能在第一次执行后再次更改,即当所有分支至少被占用一次时。
发布于 2021-12-08 09:22:19
逃逸分析(EA)在某些情况下工作:
在方法中声明的objects.
G 211
下面的测试用例将证明EA有效。
代码
/**
* @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
# 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# 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/opGC测井
无EA
[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[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
With EA的运行时间是,比调用 withEA()的次数少得多(因为这个测试是在7.2s之后),EA工作,然后GC不再发生。相反,withoutEA()发生GC整个时间.由此得出的结论是EA工作并采取了一些措施来减少GC (可能是在堆栈上创建对象,或者一次性销毁堆上的对象),从而使java程序更高效地运行。
https://stackoverflow.com/questions/70223634
复制相似问题