Статьи

Что вызывает OutOfMemoryError?

OutOfMemoryError может быть OutOfMemoryError при возникновении одного из следующих обстоятельств:

  • JVM не хватает родной памяти
  • Кучи Java не хватает памяти
  • PermGen или Metaspace не хватает памяти
  • JVM потратила слишком много времени, пытаясь собрать мусор

Основная причина OutOfMemoryError обычно может быть вычтена из сообщения об ошибке. Давайте посмотрим на детали каждого из обстоятельств.

JVM не хватает родной памяти

В основном это означает, что объем памяти, выделенной для JVM, исчерпан. Максимальный размер процесса для 32-разрядной JVM составляет примерно 3,5–4 ГБ. Если он превышен, OutOfMemoryError будет брошен. Даже в 64-разрядной JVM, когда JVM запрашивает больше памяти, операционной системе может просто не хватать ее. Посмотрите на следующий фрагмент:

01
02
03
04
05
06
07
08
09
10
11
for (int i = 0; true; ++i) {
  new Thread() {
    public void run() {
      try {
         Thread.sleep(1000000);
      } catch(InterruptedException e) { }
    }
   }.start();
 
   System.out.println("Thread"; + i + "created");
}

На моем ноутбуке (64-битная Mac OS X 10.11.6 с Java 1.8.0_112) JVM аварийно завершает работу после создания 2023 потоков:

1
2
3
4
Thread 2021 created
Thread 2022 created
Thread 2023 created
Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread

Кучи Java не хватает памяти

Это довольно очевидно. Слишком много объектов выделено, поэтому они не помещаются в пространство кучи, настроенное для JVM. Увеличение размера кучи звучит как решение, но если оно вызвано утечкой памяти, оно просто OutOfMemoryError . Сообщение об ошибке довольно ясно:

1
Exception in thread “main” java.lang.OutOfMemoryError: Java heap space

PermGen или Metaspace не хватает памяти

PermGen (Java 7 и более ранние версии) имеет ограниченный максимальный размер . Это означает, что если загружено слишком много классов, PermGen может заполниться, и OutOfMemoryError будет брошен. Увеличение максимального размера PermGen должно помочь. Java 8 не имеет PermGen, но вместо этого Metaspace. По умолчанию он имеет неограниченный максимальный размер, поэтому до тех пор, пока вы не установите ограничение с MaxMetaspaceSize флага MaxMetaspaceSize , ошибка не должна возникать. Чтобы диагностировать OutOfMemoryError вызванную PermGen или Metaspace, следует изучить сообщение об ошибке:

1
2
Exception in thread “main” java.lang.OutOfMemoryError: PermGen space
Exception in thread “main” java.lang.OutOfMemoryError: Metaspace

JVM потратила слишком много времени, пытаясь собрать мусор

Это самый OutOfMemoryErrorOutOfMemoryError когда GC тратит слишком много времени на сбор мусора с слишком маленьким результатом, а дальнейшее выполнение приложения не имеет смысла. Другими словами, должны быть выполнены все следующие условия :

  • Более 98% времени уходит в GC (98% является значением по умолчанию, оно может быть переопределено GCTimeLimit=N )
  • Менее 2% кучи восстанавливается во время полного GC (опять же, 2% является значением по умолчанию, оно может быть переопределено GCHeapFreeLimit=N )
  • Оба условия, упомянутые выше, выполняются в течение пяти последовательных полных циклов ГХ
  • Флаг UseGCOverheadLimit не отключен (значение по умолчанию — true)

Запуск полного GC означает, что JVM не хватает памяти в любом случае. Если 98% времени тратится на освобождение только 2% кучи, то это означает, что ЦП почти полностью занят GC и практически никакая логика приложения не может быть выполнена. Вот почему имеет смысл отказаться и выбросить OutOfMemoryError со следующим сообщением:

1
Exception in thread “main” java.lang.OutOfMemoryError: GC overhead limit exceeded
Ссылка: Что вызывает OutOfMemoryError? от нашего партнера JCG Гжегожа Мирека в > блоге исполнителя code_ .