Анализ побега в Java
Насколько я знаю, JVM использует анализ выхода для некоторых оптимизаций производительности, таких как укрупнение блокировки и устранение блокировки. Мне интересно, есть ли возможность для JVM решить, что любой конкретный объект может быть размещен в стеке с помощью escape-анализа.
Некоторые ресурсы заставляют меня думать, что я прав. Есть ли JVM, которые на самом деле это делают?
3 ответа
Я не думаю, что он избегает анализа для размещения стека. пример:
public class EscapeAnalysis {
private static class Foo {
private int x;
private static int counter;
public Foo() {
x = (++counter);
}
}
public static void main(String[] args) {
System.out.println("start");
for (int i = 0; i < 10000000; ++i) {
Foo foo = new Foo();
}
System.out.println(Foo.counter);
}
}
с -server -verbose:gc -XX+DoEscapeAnalysis
:
Начните [GC 3072K-> 285K (32640K), 0,0065187 с] [GC 3357K-> 285K (35712K), 0,0053043 с] [GC 6429K-> 301K (35712K), 0,0030797 с] [GC 6445K-> 285K (41856K), 0,0033648 с] [GC 12573K-> 285K (41856K), 0,0050432 секунды] [GC 12573K-> 301K (53952K), 0,0043682 с] [GC 24877K-> 277K (53952K), 0,0031890 с] [GC 24853K-> 277K (78528K), 0,0005293 с] [GC 49365K-> 277K (78592K), 0,0006699 секунд] 10000000
Предположительно JDK 7 поддерживает выделение стека.
С этой версией java -XX:+DoEscapeAnalysis обеспечивает намного меньшую активность gc и ускоряет выполнение в 14 раз.
$ java -version
java version "1.6.0_14"
Java(TM) SE Runtime Environment (build 1.6.0_14-b08)
Java HotSpot(TM) Client VM (build 14.0-b16, mixed mode, sharing)
$ uname -a
Linux xxx 2.6.18-4-686 #1 SMP Mon Mar 26 17:17:36 UTC 2007 i686 GNU/Linux
Без анализа побега,
$ java -server -verbose:gc EscapeAnalysis|cat -n
1 start
2 [GC 896K->102K(5056K), 0.0053480 secs]
3 [GC 998K->102K(5056K), 0.0012930 secs]
4 [GC 998K->102K(5056K), 0.0006930 secs]
--snip--
174 [GC 998K->102K(5056K), 0.0001960 secs]
175 [GC 998K->102K(5056K), 0.0002150 secs]
176 10000000
С анализом побега,
$ java -server -verbose:gc -XX:+DoEscapeAnalysis EscapeAnalysis
start
[GC 896K->102K(5056K), 0.0055600 secs]
10000000
Время выполнения значительно сокращается при анализе побега. Для этого цикл был изменен на 10e9 итераций,
public static void main(String [] args){
System.out.println("start");
for(int i = 0; i < 1000*1000*1000; ++i){
Foo foo = new Foo();
}
System.out.println(Foo.counter);
}
Без анализа побега,
$ time java -server EscapeAnalysis
start
1000000000
real 0m27.386s
user 0m24.950s
sys 0m1.076s
С анализом побега,
$ time java -server -XX:+DoEscapeAnalysis EscapeAnalysis
start
1000000000
real 0m2.018s
user 0m2.004s
sys 0m0.012s
Таким образом, при анализе побега пример выполнялся примерно в 14 раз быстрее, чем анализ без побега.
Анализ побега действительно хорош, но он не является полной картой без тюрьмы. если у вас есть динамически изменяемая коллекция внутри объекта, анализ escape НЕ переключается с кучи на стек. Например:
public class toEscape {
public long l;
public List<Long> longList = new ArrayList<Long>();
}
Даже если этот объект создан в методе и абсолютно НЕ сбегает с синтаксической точки зрения, компилятор не пометит это как escape. Я подозреваю, потому что этот longList на самом деле не ограничен по размеру с чисто синтаксической точки зрения и потенциально может взорвать ваш стек. Таким образом, я считаю, что это дело проходит. Я экспериментировал с этим, когда longList был пуст, и все же он вызывал коллекции в простом микро-тесте.