Причины нехватки памяти JVM
Вопросы основаны на Oracle Hotspot JDK8.
Когда приложение сталкивается с java.lang.OutOfMemory: Java heap space
исключением, я полагаю, есть две возможные причины.
- Выделенный размер кучи JVM достигает
-Xmx
указанного размера, и система GC не может выжать достаточно места. - Выделенная куча JVM не достигает
-Xmx
, но недостаточно физической памяти для роста кучи JVM. Предположим, что-Xms
<-Xmx
.
Я знаю, 1
что это причина, по которой JVM выбрасывает java.lang.OutOfMemory: Java heap space
исключение. Есть 2
разумная причина?
Я нахожу упомянутые статьиjava.lang.OutOfMemoryError: native memory exhausted
, но все они ограничены веб-сайтом IBM. Ограничено ли это ожиданием реализованной IBM JVM или это стандартное ожидание в Спецификации JVM?
Я провел несколько экспериментов с кодом, предоставленным @Eugene в ответ. Как отметил @Holger, результат варьируется в разных средах. Я тестировал как CentOS x64, так и Win7 x64 с Hotspot JDK8 x64. Для простоты своп и виртуальная память отключены.
Я увеличиваю границы памяти (-Xmx и -Xms) шаг за шагом.
I. -Xmx < доступная логическая память
- И в CentOS, и в Windows отображается OutOfMemoryError: пространство кучи Java.
II. доступная логическая память <-Xmx < максимальная физическая память
- CentOS: GC пытается заполнить сборщик мусора несколько раз, с ошибкой выделения , и процесс завершается системой, оставляя сообщение Killed .
- Windows: GC пытается выполнить полное обслуживание GC несколько раз, с ошибкой выделения и выбросить OutOfMemoryError: пространство кучи Java
III. -Xmx> максимальная физическая память
- CentOS: то же, что и во II
- Окна: такие же, как во II
IV. -Xms> максимальная физическая память
- CentOS: кажется, что JVM не запускается. Сообщение об ошибке выглядит так:
Предупреждение виртуальной машины 64-разрядного сервера Java HotSpot (TM): Ошибка INFO: os :: commit_memory (0x00000000e62a0000, 349569024, 0); error = 'Невозможно выделить память' (errno = 12)
- Windows: не удалось запустить JVM. Сообщение об ошибке выглядит так:
Произошла ошибка при инициализации виртуальной машины. Не удалось зарезервировать достаточно места для кучи объектов.
Итак, одна и та же JVM по-разному ведет себя в разных ОС.
- В Windows ОС не убивает JVM. И JVM всегда выбрасывает OutOfMemoryError: пространство кучи Java, когда использование памяти превышает.
- В Linux ОС завершает процессы, когда не хватает памяти.
- На обеих ОС JVM не запускалась, когда доступная память не удовлетворяет минимальным требованиям JVM.
Ответы
Прежде всего, есть больше причин для сбоя сборщика мусора с «нехваткой памяти», как объясняется в комментарии под вашим вопросом.
Доказать точку номер (2) легко, просто создайте код, который всегда выделяет:
public static void main(String[] args) {
test(1);
}
static void test(int x){
List<byte[]> list = new ArrayList<>();
while(x == 1){
byte [] b =new byte[1 * 1024 * 1024];
b[100] = 42;
list.add(b);
}
System.out.println(list.hashCode());
}
И запустите это -Xms1g -Xmx100g
в системе, в которой меньше 100g
ОЗУ. Вы можете включить журналы GC (я сделал это, например, с помощью "-Xlog:heap*=debug" "-Xlog:gc*=debug"
флагов в java-9) и посмотреть, как сильно GC пытается справиться с этим постоянным распределением, в конечном итоге терпя неудачу.