Как происходит аллокация памяти перед выбрасыванием OutOfMemoryError в Java?

«Как происходит аллокация памяти перед выбрасыванием OutOfMemoryError в Java?» — вопрос из категории Java Core, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

OutOfMemoryError (OOM) — критическая ошибка, возникающая, когда JVM не может выделить память для нового объекта, и сборщик мусора (GC) не может освободить достаточное количество памяти.

Процесс аллокации и эскалации до OOM:

  1. Запрос на аллокацию: JVM пытается выделить память в куче (heap) для создания нового объекта.
  2. Нехватка памяти: Если в текущем поколении (Eden Space) недостаточно свободного непрерывного пространства, запускается малая сборка мусора (Minor GC).
  3. Эскалация: Если после Minor GC памяти всё ещё не хватает, JVM пытается выделить объект непосредственно в старом поколении (Old Generation). При неудаче запускается полная сборка мусора (Full GC).
  4. Расшение кучи: Если после Full GC памяти недостаточно, JVM пытается расширить размер кучи в пределах, заданных параметром -Xmx (максимальный размер).
  5. Исключение: Если расширение невозможно (достигнут -Xmx) или не помогает, JVM выбрасывает OutOfMemoryError.

Пример, вызывающий OOM в куче:

// Запуск: java -Xmx10m -Xms10m OOMDemo
import java.util.ArrayList;
import java.util.List;

public class OOMDemo {
    public static void main(String[] args) {
        List<Object> leak = new ArrayList<>();
        while (true) {
            leak.add(new byte[1024 * 1024]); // Аллокация по 1 МБ
        }
    }
}

Основные типы OutOfMemoryError:

  • Java heap space — классическая нехватка памяти в куче для объектов.
  • Metaspace — исчерпана память для метаданных классов (заменяет PermGen).
  • Direct buffer memory — переполнена память, выделенная для NIO ByteBuffer.allocateDirect().
  • Unable to create new native thread — превышен лимит на количество потоков ОС.
  • Requested array size exceeds VM limit — попытка создать массив размером больше Integer.MAX_VALUE - 2 или доступной памяти.

Best Practice: Для диагностики используйте профилировщики (VisualVM, JProfiler) и анализируйте дампы кучи, создаваемые с помощью параметров -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path/to/dump.hprof.