Что такое OutOfMemoryError?
OutOfMemoryError в Java — это ошибка уровня Error, которая возникает, когда виртуальная машина (JVM) не может выделить память для нового объекта, потому что все доступные ресурсы исчерпаны и Garbage Collector не может освободить достаточно пространства.
📌 Где возникает OutOfMemoryError
Java-heap состоит из нескольких областей, и OutOfMemoryError может возникать в разных местах — в зависимости от причины.
Основные типы ошибок:
Вид ошибки | Когда возникает |
---|---|
java.lang.OutOfMemoryError: Java heap space | Закончилась память в куче (heap) |
--- | --- |
java.lang.OutOfMemoryError: GC overhead limit exceeded | GC слишком часто работает и почти не освобождает память |
--- | --- |
java.lang.OutOfMemoryError: Metaspace | Переполнена область метаданных классов (с Java 8+) |
--- | --- |
java.lang.OutOfMemoryError: Direct buffer memory | Используется слишком много off-heap памяти (например, через ByteBuffer) |
--- | --- |
java.lang.OutOfMemoryError: unable to create new native thread | Не удаётся создать новый поток из-за нехватки системных ресурсов |
--- | --- |
🔍 Почему возникает OutOfMemoryError
1. Утечки памяти (Memory Leaks)
Объекты, на которые сохраняются ссылки, несмотря на ненадобность, не могут быть удалены GC. Например:
-
Статические коллекции (static List)
-
Неправильно реализованные кеши
-
Незакрытые слушатели (например, в Android: LiveData.observe)
-
Вложенные классы, захватывающие контекст Activity
2. Создание слишком большого количества объектов
List<int\[\]> list = new ArrayList<>();
while (true) {
list.add(new int\[1_000_000\]);
}
→ Быстрое потребление кучи → OutOfMemoryError.
3. Рекурсия без выхода (стековый overflow)
Хоть это технически вызывает StackOverflowError, но при создании новых объектов в глубокой рекурсии также можно получить OutOfMemoryError.
4. Слишком много потоков
while (true) {
new Thread(() -> {}).start();
}
→ OutOfMemoryError: unable to create new native thread
5. Метаданные классов (до Java 8 — PermGen, с Java 8 — Metaspace)
-
Слишком много загруженных классов (например, при создании новых ClassLoader)
-
Динамическая генерация прокси и лямбд
→ OutOfMemoryError: Metaspace
🛠 Как диагностировать и решать
1. Увеличить память JVM
Пример для увеличения heap:
java -Xmx1024m MyApp
Другие флаги:
-
-Xms — начальный размер кучи
-
-Xmx — максимальный размер кучи
-
-XX:MaxMetaspaceSize=... — лимит Metaspace
2. Профилирование и поиск утечек
Используйте:
- **VisualVM
** - **YourKit
** - **JProfiler
** - **MAT (Memory Analyzer Tool)
**
→ они помогут выявить объекты, которые не освобождаются.
3. Использование слабых/мягких ссылок
Если объект можно восстановить, лучше использовать WeakReference или SoftReference, чтобы не мешать GC.
4. Оптимизация кода
-
Избегать больших массивов в памяти
-
Не кэшировать всё подряд
-
Закрывать потоки, слушатели и Context-зависимые ресурсы
-
Использовать try-with-resources для автоматического освобождения
❗ OutOfMemoryError — это ошибка, а не исключение
-
Это unchecked ошибка (extends Error)
-
Не должна ловиться в обычных условиях (catch (Throwable) — допустимо, но опасно)
-
Обычно требует **пересмотра архитектуры и логики работы с памятью
**
🧠 Кратко
OutOfMemoryError означает, что JVM не может выделить память, потому что:
-
Объекты не удаляются (утечки)
-
Память заканчивается из-за перегрузки
-
Лимиты памяти слишком малы
Решение требует анализа причин и оптимизации управления памятью в приложении.