JavaのEscape AnalysisでNoEscapeがGlobalEscapeになってしまうケースの検証

Java ™ HotSpot Virtual Machine Performance Enhancements

Escape analysis is supported and enabled by default in Java SE 6u23 and later.

ということで最近のJVMでは有効になっているEscape analysisですが、こんな記事を見つけました。


Richard Burnison - Escape Analysis & Stack Allocated Objects

NoEscapeについて説明しているくだりで

Important to note, however, is that objects requiring finalizer execution are considered GlobalEscape and will not be stack-bound.

という記述があり、ちょっと気になったので試してみました。

$ java -version
java version "1.7.0_45"
Java(TM) SE Runtime Environment (build 1.7.0_45-b18)
Java HotSpot(TM) 64-Bit Server VM (build 24.45-b08, mixed mode)

まずは普通に。

public class Main {
    void run() {
        int totalLen = 0;
        for (int i = 0; i < 100000000; i++) {
            String s = new String("hello");
            totalLen += s.length();
        }
        System.out.println(totalLen);
    }

    public static void main(String argv[]) {
        Main main = new Main();
        main.run();
    }
}
  • Escape analysis無し
$ java -Xms512M -Xmx512M -XX:+PrintGCDetails -verbose:gc -XX:-DoEscapeAnalysis Main
[GC [PSYoungGen: 132096K->432K(153600K)] 132096K->440K(503296K), 0.0030680 secs] [Times: user=0.00 sys=0.01, real=0.01 secs] 
50000000
Heap
 PSYoungGen      total 153600K, used 111393K [0x00000007f5500000, 0x0000000800000000, 0x0000000800000000)
  eden space 132096K, 84% used [0x00000007f5500000,0x00000007fc15c698,0x00000007fd600000)
  from space 21504K, 2% used [0x00000007fd600000,0x00000007fd66c010,0x00000007feb00000)
  to   space 21504K, 0% used [0x00000007feb00000,0x00000007feb00000,0x0000000800000000)
 ParOldGen       total 349696K, used 8K [0x00000007dff80000, 0x00000007f5500000, 0x00000007f5500000)
  object space 349696K, 0% used [0x00000007dff80000,0x00000007dff82000,0x00000007f5500000)
 PSPermGen       total 21504K, used 2554K [0x00000007dad80000, 0x00000007dc280000, 0x00000007dff80000)
  object space 21504K, 11% used [0x00000007dad80000,0x00000007daffebf8,0x00000007dc280000)
  • Escape analysis有り
$ java -Xms512M -Xmx512M -XX:+PrintGCDetails -verbose:gc -XX:+DoEscapeAnalysis Main
50000000
Heap
 PSYoungGen      total 153600K, used 5284K [0x00000007f5500000, 0x0000000800000000, 0x0000000800000000)
  eden space 132096K, 4% used [0x00000007f5500000,0x00000007f5a29028,0x00000007fd600000)
  from space 21504K, 0% used [0x00000007feb00000,0x00000007feb00000,0x0000000800000000)
  to   space 21504K, 0% used [0x00000007fd600000,0x00000007fd600000,0x00000007feb00000)
 ParOldGen       total 349696K, used 0K [0x00000007dff80000, 0x00000007f5500000, 0x00000007f5500000)
  object space 349696K, 0% used [0x00000007dff80000,0x00000007dff80000,0x00000007f5500000)
 PSPermGen       total 21504K, used 2552K [0x00000007dad80000, 0x00000007dc280000, 0x00000007dff80000)
  object space 21504K, 11% used [0x00000007dad80000,0x00000007daffe060,0x00000007dc280000)

Escape analysis有りの場合、Eden領域の使用量が少なくHeapではなくStack allocationになっているものと推測。

では、finalize() 実装版

public class Main {
    static int finalizeCount = 0;

    void run() {
        int totalLen = 0;
        for (int i = 0; i < 10000000; i++) {
            String s = new String("hello");
            totalLen += s.length();
        }
        System.out.println(totalLen);
        System.out.println(finalizeCount);
    }

    @Override
    protected void finalize() throws Throwable {
        finalizeCount++;
        super.finalize();
    }

    public static void main(String argv[]) {
        Main main = new Main();
        main.run();
    }
}

これをEA有効にして実行してみると

$ java -Xms512M -Xmx512M -XX:+PrintGCDetails -verbose:gc -XX:+DoEscapeAnalysis Main
50000000
0
Heap
 PSYoungGen      total 153600K, used 5284K [0x00000007f5500000, 0x0000000800000000, 0x0000000800000000)
  eden space 132096K, 4% used [0x00000007f5500000,0x00000007f5a29028,0x00000007fd600000)
  from space 21504K, 0% used [0x00000007feb00000,0x00000007feb00000,0x0000000800000000)
  to   space 21504K, 0% used [0x00000007fd600000,0x00000007fd600000,0x00000007feb00000)
 ParOldGen       total 349696K, used 0K [0x00000007dff80000, 0x00000007f5500000, 0x00000007f5500000)
  object space 349696K, 0% used [0x00000007dff80000,0x00000007dff80000,0x00000007f5500000)
 PSPermGen       total 21504K, used 2552K [0x00000007dad80000, 0x00000007dc280000, 0x00000007dff80000)
  object space 21504K, 11% used [0x00000007dad80000,0x00000007daffe2f0,0x00000007dc280000)

と、finalize() 実装前と変わらないので、"objects requiring finalizer execution are considered GlobalEscape and will not be stack-bound" というのは(少なくとも1.7.0_45では)気にしなくて良さそうかなぁと思います。