Skip to content

Instantly share code, notes, and snippets.

@navyxliu
Last active December 15, 2022 00:06
Show Gist options
  • Save navyxliu/ee4465e2146ef99c5ae1fa1ba6b70e25 to your computer and use it in GitHub Desktop.
Save navyxliu/ee4465e2146ef99c5ae1fa1ba6b70e25 to your computer and use it in GitHub Desktop.
Example2.java
// -Xcomp -Xms16M -Xmx16M -XX:+AlwaysPreTouch -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC -XX:-UseOnStackReplacement -XX:CompileOnly='Example2.foo' -XX:CompileCommand=dontinline,Example2.blackhole
class Example2 {
private Object _cache;
public Object foo(boolean cond) {
Object x = new Object();
blackhole();
if (cond) {
_cache = x;
}
return x;
}
public static void blackhole() {}
public static void main(String[] args) {
Example2 kase = new Example2();
// Epsilon Test:
// By setting the maximal heap and use EpsilonGC, let's see how long and how many iterations the program can sustain.
// if PEA manages to reduce allocation rate, we expect the program to stay longer.
// Roman commented it with a resonable doubt: "or your code slow down the program..."
// That's why I suggest to observe iterations. It turns out not trivial because inner OOME will implode hotspot. We don't have a chance to execute the final statement...
long iterations = 0;
try {
while (true) {
kase.foo(0 == (iterations & 0xf));
iterations++;
}
} finally {
System.err.println("Epsilon Test: " + iterations);
}
}
}
@navyxliu
Copy link
Author

navyxliu commented Nov 8, 2022

C2 generates code like this after parser with -XX:+DoPartialEscapeAnalysis.

public void foo(boolean cond1, boolean cond2) {
    Object x0 = new Object();

    blackhole();

    if (cond1) {
        x1 = new Object();
        _cache1 = x1;
    }
    x2 = phi(x2=new Object(), x1);
    blackhole();

    if (cond2) {
        _cache2 = x2;
    }
}

if cond1 == true && cond2== true, then we have _cache1 == __cache2 == x1

@navyxliu
Copy link
Author

navyxliu commented Nov 8, 2022

@merykitty

Thank you for vetting this. here is a modified program from and enable assertion.
We can verify that cache1 == cache2.

The reason is explained above. One thing is worth noting: when we parse "if(cond2) ...", object x has been materialized. PEA won't materialize it again at "_cache2 = x".

// -ea -Xcomp -Xms16M -Xmx16M -XX:+AlwaysPreTouch -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC -XX:-UseOnStackReplacement -XX:CompileOnly='Example2_merykitty.foo' -XX:CompileCommand=dontinline,Example2_merykitty.blackhole -XX:+DoPartialEscapeAnalysis

class Example2_merykitty {
    private static Object _cache1;
    private static Object _cache2;

    public void foo(boolean cond1, boolean cond2) {
        Object x = new Object();
    
        blackhole();
    
        if (cond1) {
            _cache1 = x;
        }
    
        blackhole();
    
        if (cond2) {
            _cache2 = x;
        }
    }

    public static void blackhole() {}
    
    public static void main(String[] args)  {
        Example2_merykitty kase = new Example2_merykitty();
        long iterations = 0;
        try {
            while (true) {
		boolean cond = 0 == (iterations & 0xf);
                kase.foo(cond, cond);
                assert Example2_merykitty._cache1 == Example2_merykitty._cache2 :"check";
                iterations++;
            }
        } finally {
            System.err.println("Epsilon Test: " + iterations);
        }
    }
}

@navyxliu
Copy link
Author

Link to Example3_1. It features non-trivial object with stateful fields and inlined methods.
https://gist.github.com/navyxliu/74d0546004a773cb5219754f6ed63d43

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment