Причины нехватки памяти JVM

Aug 20 2020

Вопросы основаны на Oracle Hotspot JDK8.

Когда приложение сталкивается с java.lang.OutOfMemory: Java heap spaceисключением, я полагаю, есть две возможные причины.

  1. Выделенный размер кучи JVM достигает -Xmxуказанного размера, и система GC не может выжать достаточно места.
  2. Выделенная куча 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.

Ответы

1 Eugene Aug 20 2020 at 18:04

Прежде всего, есть больше причин для сбоя сборщика мусора с «нехваткой памяти», как объясняется в комментарии под вашим вопросом.

Доказать точку номер (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 пытается справиться с этим постоянным распределением, в конечном итоге терпя неудачу.