どのオブジェクトがスタック割り当てに適しているかをよりよく理解するために、Java 7 でエスケープ分析を使用していくつかのテストを行っています。
スタック割り当てをテストするために書いたコードは次のとおりです。
import java.util.ArrayList;
import java.util.Iterator;
public class EscapeAnalysis {
private static final long TIME_TO_TEST = 10L * 1000L; // 10s
static class Timestamp {
private long millis;
public Timestamp(long millis) {
this.millis = millis;
}
public long getTime() {
return millis;
}
public void setTime(long time) {
millis = time;
}
}
public static void main(String[] args) {
long r = 0;
System.out.println("test1");
r += test1();
System.out.println("test2");
r += test2();
System.out.println("test3");
r += test3();
System.out.println("test4");
r += test4();
System.out.println("test5");
r += test5();
System.out.println("test6");
r += test6();
System.out.println(r);
}
public static long test1() {
long r = 0;
long start = System.currentTimeMillis();
while (System.currentTimeMillis() - start < TIME_TO_TEST) {
r += new Timestamp(System.currentTimeMillis()).getTime();
}
return r;
}
public static long test2() {
ArrayList<Integer> l = new ArrayList<Integer>(1000);
for (int i = 0; i < 1000; ++i) {
l.add(i);
}
long r = 0;
long start = System.currentTimeMillis();
while (System.currentTimeMillis() - start < TIME_TO_TEST) {
for (Iterator<Integer> it = l.iterator(); it.hasNext(); ) {
r += it.next().longValue();
}
}
return r;
}
public static long test3() {
long r = 0;
long start = System.currentTimeMillis();
while (System.currentTimeMillis() - start < TIME_TO_TEST) {
Timestamp ts = new Timestamp(System.currentTimeMillis());
ts.setTime(42);
r += ts.getTime();
}
return r;
}
public static long test4() {
ArrayList<Integer> l = new ArrayList<Integer>(1000);
for (int i = 0; i < 1000; ++i) {
l.add(i);
}
long r = 0;
long start = System.currentTimeMillis();
while (System.currentTimeMillis() - start < TIME_TO_TEST) {
Iterator<Integer> it = l.iterator();
r += it.next().longValue();
r += it.next().longValue();
r += it.next().longValue();
r += it.next().longValue();
}
return r;
}
public static long test5() {
ArrayList<Integer> l = new ArrayList<Integer>(1000);
for (int i = 0; i < 1000; ++i) {
l.add(i);
}
long r = 0;
long start = System.currentTimeMillis();
while (System.currentTimeMillis() - start < TIME_TO_TEST) {
Iterator<Integer> it = l.iterator();
for (int i = 0; i < l.size(); ++i) {
r += it.next().longValue();
}
}
return r;
}
public static long test6() {
long r = 0;
long start = System.currentTimeMillis();
while (System.currentTimeMillis() - start < TIME_TO_TEST) {
for (Timestamp ts = new Timestamp(System.currentTimeMillis());
ts.getTime() > 0;
ts.setTime(ts.getTime() + System.currentTimeMillis())) {
r += ts.getTime();
}
}
return r;
}
}
そして、これがLinux上のJava 7で出力されるものです
java -server -version
java version "1.7.0_02"
Java(TM) SE Runtime Environment (build 1.7.0_02-b13)
Java HotSpot(TM) 64-Bit Server VM (build 22.0-b10, mixed mode)
java -server -verbose:gc -XX:CompileThreshold=1 -cp bin EscapeAnalysis
test1
test2
[GC 15616K->352K(59776K), 0,0014270 secs]
[GC 15968K->288K(59776K), 0,0011790 secs]
[GC 15904K->288K(59776K), 0,0018170 secs]
[GC 15904K->288K(59776K), 0,0011100 secs]
[GC 15904K->288K(57152K), 0,0019790 secs]
[GC 15520K->320K(56896K), 0,0011670 secs]
[GC 15232K->284K(56256K), 0,0011440 secs]
test3
test4
test5
[GC 14876K->348K(55936K), 0,0005340 secs]
[GC 14620K->348K(56000K), 0,0004560 secs]
[GC 14300K->316K(55296K), 0,0004680 secs]
[GC 13948K->316K(55488K), 0,0003590 secs]
[GC 13692K->316K(54784K), 0,0004580 secs]
[GC 13436K->316K(54976K), 0,0005430 secs]
[GC 13180K->316K(54272K), 0,0004500 secs]
[GC 12924K->316K(54464K), 0,0005090 secs]
[GC 12668K->316K(53760K), 0,0004490 secs]
[GC 12412K->316K(53888K), 0,0004350 secs]
[GC 12156K->316K(53312K), 0,0005060 secs]
test6
6737499643744733086
オブジェクトがスタックに割り当てられたかどうかを知るために GC ログを使用しています ( Java での Escape 分析からのアイデア)。
出力に基づいて、スタック割り当てはtest1、test3、test4、およびtest6で機能し、test2およびtest5では機能しません。機能するのに for ループのイテレータでこれが機能しない理由がわかりません
- for ループの外側の反復子 (test4 を参照)、
- for ループ内で別のオブジェクトを使用する (test6 を参照)。
ArrayList イテレータのコードを読みましたが、現在のメソッドも現在のスレッドもエスケープしないため、テスト 2 と 5 でスタック割り当ての対象にならない理由がわかりません。
何か案が?