Какие типы OutOfMemoryError существуют
Класс java.lang.OutOfMemoryError (OOME) является подклассом VirtualMachineError и сигнализирует о невозможности виртуальной машины выделить необходимый объем памяти. Конкретная...
🟢 Junior Level
Класс java.lang.OutOfMemoryError (OOME) является подклассом VirtualMachineError и сигнализирует о невозможности виртуальной машины выделить необходимый объем памяти. Конкретная причина ошибки всегда указывается в строке сообщения (detail message).
Основные типы OOME, встречающиеся на практике:
| Тип сообщения OOME | Область памяти / Причина | Что означает простыми словами |
|---|---|---|
Java heap space |
Java Heap | Закончилась куча для обычных Java-объектов. |
GC overhead limit exceeded |
CPU / Garbage Collector | Сборщик мусора тратит всё процессорное время, но почти ничего не освобождает. |
Metaspace |
Native Memory (Метаданные классов) | Загружено слишком много классов или произошла утечка ClassLoader. |
Direct buffer memory |
Off-Heap (NIO буферы) | Исчерпан лимит прямой памяти, выделяемой через ByteBuffer.allocateDirect(). |
unable to create new native thread |
Потоки ОС / Стек | ОС не позволяет процессу создать новый поток (закончилась память под стеки или превышен системный лимит). |
Requested array size exceeds VM limit |
Java Heap (Массивы) | Попытка создать массив с размером больше допустимого лимита JVM. |
🟡 Middle Level
1. java.lang.OutOfMemoryError: Java heap space
- Механика: Поток запрашивает память под новый объект или массив в куче, но доступного непрерывного пространства нет даже после вызова Full GC и очистки ссылок
SoftReference. - Основные причины:
- Реальная утечка памяти (Memory Leak): объекты больше не нужны, но на них остаются сильные ссылки из статических полей, долгоживущих кэшей или коллекций.
- Недостаточный максимальный размер кучи: параметры
-Xmxили-XX:MaxRAMPercentageзаданы слишком малыми для пиковой бизнес-нагрузки.
- Диагностика и устранение: Снять Heap Dump (
-XX:+HeapDumpOnOutOfMemoryError) и проанализировать граф доминаторов в Eclipse Memory Analyzer (MAT) или IDEA Profiler.
2. java.lang.OutOfMemoryError: GC overhead limit exceeded
- Механика: Защитная эвристика HotSpot JVM. Если сборщик мусора тратит более 98% времени процессора на выполнение пауз GC и при этом освобождает менее 2% памяти кучи на протяжении 5 последовательных циклов, JVM выбрасывает эту ошибку.
- Цель эвристики: Предотвратить состояние thrashing (паралич приложения), когда 100% CPU уходит на бесполезный сбор мусора, а полезная работа не выполняется.
- Частая ошибка: Отключение эвристики флагом
-XX:-UseGCOverheadLimit. Это антипаттерн: приложение просто зависнет в 100% CPU на десятки минут и в итоге всё равно упадёт сJava heap space.
3. java.lang.OutOfMemoryError: Metaspace
- Механика: Память Metaspace располагается в Native Memory процесса (вне кучи) и хранит структуры метаданных классов (
Klass, таблицы виртуальных методовvtable, константные пулы). Ошибка возникает при превышении флага-XX:MaxMetaspaceSizeили исчерпании физической RAM хоста. - Причины:
- Динамическая кодогенерация (CGLIB, ByteBuddy, Spring AOP, Hibernate, Groovy/JRuby скрипты), создающая бесконечное количество новых классов.
- Утечка загрузчиков классов (
ClassLoader Leak): старыйClassLoaderне может быть собран GC, удерживая тысячи метаданных загруженных классов.
- Диагностика:
jcmd <pid> VM.metaspaceи анализ ClassLoader-ов в профилировщике.
4. java.lang.OutOfMemoryError: Direct buffer memory
- Механика: Выделение памяти через
ByteBuffer.allocateDirect()(NIO) обращается к нативному вызовуmalloc(). HotSpot отслеживает суммарный объем выделенной прямой памяти через классjdk.internal.misc.VM / Bits. Если суммарный объем превышает лимит-XX:MaxDirectMemorySize(по умолчанию равен размеру-Xmx), бросается данная ошибка. - Причины: Утечки буферов в сетевых библиотеках (Netty, gRPC, Kafka client), когда буферы не освобождаются вызовом
ReferenceCountUtil.release()или не успевают очищаться черезCleaner/ PhantomReference. - Особенность: Память в Java Heap при этом может быть заполнена всего на 5–10%!
5. java.lang.OutOfMemoryError: unable to create new native thread
- Механика: Конструктор
new Thread().start()обращается к вызову ядра ОС (clone()илиpthread_create()в Linux). Если ядро ОС возвращает код ошибкиEAGAINилиENOMEM, JVM транслирует его в OOME. - Причины:
- Достигнут лимит потоков процесса или пользователя в ОС (
ulimit -uили/proc/sys/kernel/threads-max). - Достигнут системный лимит виртуальных областей памяти
vm.max_map_count(каждый стек потока требует 2 областиmmap— стек и guard page). - Недостаточно свободной оперативной памяти под стеки потоков (
-Xss, по умолчанию 1 МБ на поток).
- Достигнут лимит потоков процесса или пользователя в ОС (
- Парадокс: Если увеличить
-Xmx, эта ошибка проявится быстрее, так как куча займет больше памяти, оставив меньше нативной памяти под стеки потоков.
🔴 Senior Level
1. Редкие и низкоуровневые типы OOME
+-------------------------------------------------------------------------------+
| Спектр ошибок OutOfMemoryError |
+-------------------------------------------------------------------------------+
| [Java Heap] |
| ├── Java heap space (недостаток места в Eden/Tenured) |
| ├── GC overhead limit exceeded (98% CPU на GC, <2% очищено) |
| └── Requested array size exceeds VM limit (массив > Integer.MAX_VALUE - 8) |
+-------------------------------------------------------------------------------+
| [Metaspace / Class Metadata] |
| ├── Metaspace (превышен -XX:MaxMetaspaceSize) |
| └── Compressed class space (превышен -XX:CompressedClassSpaceSize) |
+-------------------------------------------------------------------------------+
| [Native / OS Memory] |
| ├── Direct buffer memory (превышен -XX:MaxDirectMemorySize) |
| ├── unable to create new native thread (лимиты ulimit / pthreads / RAM) |
| ├── request <size> bytes for <reason>. Out of swap space? (malloc failed) |
| └── Map failed (исчерпан sysctl vm.max_map_count при MappedByteBuffer) |
+-------------------------------------------------------------------------------+
А. Compressed class space
При включенном сжатии ссылок на классы (-XX:+UseCompressedClassPointers, активно по умолчанию в 64-бит JVM) все структуры метаданных Klass размещаются в едином непрерывном 32-битном адресном пространстве размером 1 ГБ (по умолчанию, настраивается через -XX:CompressedClassSpaceSize=1g).
- Даже если общий объем Metaspace свободен и имеет запас, переполнение 1 ГБ пространства сжатых классов приведёт к:
java.lang.OutOfMemoryError: Compressed class space.
Б. Requested array size exceeds VM limit
Возникает при попытке создания массива с длиной, превышающей технический максимум JVM:
// Вызовет: java.lang.OutOfMemoryError: Requested array size exceeds VM limit
int[] hugeArray = new int[Integer.MAX_VALUE];
Заголовок массива в HotSpot требует место под служебные поля (длина массива length занимает 4 байта в заголовке объекта). Поэтому предельный размер массива составляет от Integer.MAX_VALUE - 2 до Integer.MAX_VALUE - 8 в зависимости от версии платформы.
В. request <size> bytes for <reason>. Out of swap space?
Низкоуровневый сбой аллокатора C++ (os::malloc вернул NULL). Свидетельствует о полном физическом исчерпании RAM и Swap-пространства хоста.
- Сопровождается генерацией аварийного лога
hs_err_pid.log. - Причины: утечка памяти в сторонней C/C++ библиотеке (через JNI), утечка Code Cache (компилятор JIT), или служебных структур данных сборщика мусора.
2. Сравнительная матрица локализации и устранения
| Сообщение об ошибке | Первичный шаг диагностики | Ключевые флаги конфигурации |
|---|---|---|
Java heap space |
Анализ Heap Dump в MAT / JProfiler | -Xmx, -XX:MaxRAMPercentage |
GC overhead limit |
Снятие GC-логов (-Xlog:gc*), поиск утечки |
Оптимизация аллокаций, рефакторинг |
Metaspace |
jcmd <pid> VM.metaspace show-loaders |
-XX:MaxMetaspaceSize |
Compressed class space |
Проверка генерации прокси-классов | -XX:CompressedClassSpaceSize |
Direct buffer memory |
Поиск утечек в Netty / NIO, NMT | -XX:MaxDirectMemorySize, -Dio.netty.leakDetectionLevel |
unable to create thread |
Проверка ulimit -u, подсчёт потоков jstack |
Пул потоков, Virtual Threads (Java 21+), -Xss |
Out of swap space |
Проверка памяти ОС (free -m, vmstat), NMT |
Исправление Native/JNI утечек, добавление RAM |
🎯 Шпаргалка для интервью
30-секундный ответ для собеседования
«
OutOfMemoryErrorуказывает на исчерпание конкретной области памяти, зашифрованной в тексте ошибки.Java heap spaceозначает нехватку кучи для обычных объектов, аGC overhead limit exceededсигнализирует о том, что сборщик тратит более 98% времени на очистку менее 2% памяти. ОшибкиMetaspaceиCompressed class spaceвозникают при утечках загрузчиков классов в нативной памяти.Direct buffer memoryсообщает о переполнении off-heap памяти NIO (Netty). Аunable to create new native threadуказывает на исчерпание ресурсов операционной системы: лимита потоков ядра (ulimit -u) или нехватки RAM под стеки потоков-Xss».
Вопросы повышенной сложности с ответами
-
Почему при ошибке
unable to create new native threadувеличение размера-Xmxтолько ухудшит ситуацию? Ответ: Ошибкаunable to create new native threadвызывается ядром ОС, когда не удается выделить память под стек нового потока (-Xss, по умолчанию 1 МБ) или исчерпаны системные лимиты. Стеки потоков аллоцируются в нативной памяти операционной системы, за пределами Java Heap. Увеличивая-Xmx, мы отдаем куче больше виртуального адресного пространства и физической памяти, оставляя ОС и нативным потокам ещё меньше свободного пространства. Для решения проблемы нужно наоборот уменьшать-Xmx, использовать пулы потоков, уменьшать размер стека потока (-Xss512k) или переходить на виртуальные потоки (Virtual Threads / Project Loom). -
Чем отличается
OutOfMemoryError: MetaspaceотOutOfMemoryError: Compressed class space? Ответ: Metaspace хранит все метаданные классов (методы, аннотации, байткод, таблицы виртуальных методов) в нативной памяти процесса.Compressed class space— это специальная строго непрерывная 32-битная адресная область размером по умолчанию 1 ГБ внутри общего метаспейса, в которой хранятся сжатые указатели на структурыKlassдля экономии места в заголовках объектов. Если приложение динамически генерирует десятки тысяч уникальных классов, лимит 1 ГБ сжатых классов может закончиться раньше, чем общий лимит Metaspace, порождая специализированную ошибкуCompressed class space. -
Что делает флаг
-XX:-UseGCOverheadLimitи почему его опасно отключать? Ответ: Флаг отключает защитный механизм, выбрасывающийGC overhead limit exceeded, когда сборщик мусора тратит >98% времени на освобождение <2% кучи. Если его отключить, приложение не выбросит ошибку сразу, но зависнет в непрерывных Stop-the-World паузах Full GC. Процессор будет загружен на 100%, сервисные запросы будут отваливаться по таймауту, и через некоторое время приложение всё равно упадёт с ошибкойJava heap space. -
Как может произойти
Direct buffer memoryOOME, если в Heap занято всего 50 МБ из 4 ГБ? Ответ: Прямые буферы (ByteBuffer.allocateDirect()) аллоцируются в нативной памяти ОС через вызов Cmalloc(). Они находятся вне кучи JVM. При создании буфера HotSpot проверяет лимит-XX:MaxDirectMemorySize. Если сервис (например, на Netty или Kafka) аллоцирует сетевые пакеты в native memory быстрее, чем GC успевает их утилизировать черезCleaner/фантомные ссылки, либо если счетчики ссылок Netty не уменьшаются (ByteBuf.release()), память Direct Memory исчерпывается независимо от степени заполненности Java Heap.
Типичные ошибки и Red Flags
- ❌ Утверждать, что любой OutOfMemoryError лечится увеличением
-Xmx. (Red Flag: непонимание архитектуры памяти, незнание разницы между Heap, Metaspace, Thread Stack и Direct Memory). - ❌ Предлагать отключать
-XX:-UseGCOverheadLimitкак способ решения проблем с производительностью. (Red Flag: непонимание природы трэшинга GC). - ❌ Путать
StackOverflowErrorиOutOfMemoryError: unable to create new native thread. (StackOverflowError— это исчерпание глубины стека внутри одного существующего потока из-за рекурсии;unable to create new native thread— это невозможность создать сам поток в ОС).