Java OutOfMemoryError странное поведение
предполагая, что у нас есть максимальная память 256 м, почему этот код работает:
public static void main(String... args) {
for (int i = 0; i < 2; i++)
{
byte[] a1 = new byte[150000000];
}
byte[] a2 = new byte[150000000];
}
но это один бросить ум?
public static void main(String... args) {
//for (int i = 0; i < 2; i++)
{
byte[] a1 = new byte[150000000];
}
byte[] a2 = new byte[150000000];
}
2 ответа:
чтобы держать вещи в перспективе, используйте этот код
-Xmx64m
:static long sum; public static void main(String[] args) { System.out.println("Warming up..."); for (int i = 0; i < 100_000; i++) test(1); System.out.println("Main call"); test(5_500_000); System.out.println("Sum: " + sum); } static void test(int size) { // for (int i = 0; i < 1; i++) { long[] a2 = new long[size]; sum += a2.length; } long[] a1 = new long[size]; sum += a1.length; }
в зависимости от того, делаете ли вы разминку или пропустить его, он будет дуть или не дуть. Это потому, что JITted код правильно
null
S из var, тогда как интерпретируемый код не делает. оба поведения приемлемы в соответствии со спецификацией языка Java, что означает, что вы находитесь во власти JVM с этим.Проверено с
Java HotSpot(TM) 64-Bit Server VM (build 23.3-b01, mixed mode)
на ОС Х.анализ байт-кода
посмотрите на байт-код с
for
цикл (простой код, безsum
переменной):static void test(int); Code: 0: iconst_0 1: istore_1 2: goto 12 5: iload_0 6: newarray long 8: astore_2 9: iinc 1, 1 12: iload_1 13: iconst_1 14: if_icmplt 5 17: iload_0 18: newarray long 20: astore_1 21: return
и без:
static void test(int); Code: 0: iload_0 1: newarray long 3: astore_1 4: iload_0 5: newarray long 7: astore_1 8: return
нет явного
null
ing выходит в любом случае, но обратите внимание, что в нет пример одна и та же ячейка памяти фактически используется повторно, в отличие от на пример. Это, во всяком случае, приведет к ожиданию, противоположному наблюдаемому поведение.поворот...
основываясь на том, что мы узнали из байт-кода, попробуйте запустить это:
public static void main(String[] args) { { long[] a1 = new long[5_000_000]; } long[] a2 = new long[0]; long[] a3 = new long[5_000_000]; }
никто не бросил. Прокомментируйте объявление
a2
, а он вернулся. Мы выделить больше, а занимают меньше? Посмотрите на байт-код:public static void main(java.lang.String[]); Code: 0: ldc #16 // int 5000000 2: istore_1 3: ldc #16 // int 5000000 5: newarray long 7: astore_2 8: iconst_0 9: newarray long 11: astore_2 12: ldc #16 // int 5000000 14: newarray long 16: astore_3 17: return
расположение 2, используется для
a1
, используется дляa2
. То же самое верно для кода OP, но теперь мы перезаписываем местоположение с помощью ссылка на безобидный массив нулевой длины и использование другого места для хранения ссылки на наш огромный массив.подводя итог...
спецификация языка Java не указывает, что какой-либо объект мусора должны быть собранным, и спецификация JVM только говорит, что "кадр" с локальными переменными уничтожается в целом по завершении метода. Поэтому все поведение, которое мы наблюдали, соответствует книге. Элемент невидимка состояние объекта (упоминается в документе, связанном с keppil) - это просто способ описать то, что происходит в некоторых реализациях и при некоторых обстоятельствах, но никоим образом не является каноническим поведением.
это потому что пока
a1
не находится в области после скобок, он находится в состоянии под названием невидимка пока метод возвращает.большинство современных JVMs не устанавливают переменную
a1
tonull
как только он покидает область (на самом деле, есть ли внутренние скобки или нет, даже не меняет сгенерированный байтовый код), потому что он очень неэффективен и обычно не имеет значения. Таким образом,a1
невозможно собрать мусор, пока метод не вернется.вы можете проверить это, добавив строку
a1 = null;
внутри скобок, что делает программу работать нормально.
термин невидимка и объяснение взято из старой бумаги:
http://192.9.162.55/docs/books/performance/1st_edition/html/JPAppGC.fm.html
.