← Назад к вопросам
Как можно активировать OutOfMemoryError, если закончилась память?
2.3 Middle🔥 201 комментариев
#JVM и управление памятью#Тестирование
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
OutOfMemoryError: Причины и управление памятью в Java
OutOfMemoryError (OOM) — критическая ошибка, которая возникает когда Java Virtual Machine исчерпывает доступную память. За 10+ лет работы я сталкивался с различными сценариями OOM и методами их диагностики и предотвращения.
Типы OutOfMemoryError
В Java существует несколько типов OutOfMemoryError:
1. Heap space — нехватка памяти в куче (Heap)
Мост частый вид OOM. Возникает когда Java не может выделить достаточно памяти для нового объекта в куче:
public class HeapOutOfMemoryExample {
public static void main(String[] args) {
List<byte[]> list = new ArrayList<>();
try {
while (true) {
// Создание больших объектов до исчерпания памяти
byte[] bytes = new byte[1024 * 1024]; // 1 MB
list.add(bytes);
System.out.println("Allocated: " + list.size() + " MB");
}
} catch (OutOfMemoryError e) {
System.err.println("Heap OutOfMemoryError: " + e.getMessage());
e.printStackTrace();
}
}
}
// Запуск с ограничением памяти:
// java -Xmx256m HeapOutOfMemoryExample
2. PermGen / Metaspace — нехватка памяти для метаданных класса
В Java 7 и ниже это был PermGen, с Java 8+ это Metaspace:
import java.util.ArrayList;
import java.util.List;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MetaspaceOutOfMemoryExample {
public static void main(String[] args) {
List<Class<?>> classList = new ArrayList<>();
try {
while (true) {
// Динамическое создание классов
ClassLoader loader = new CustomClassLoader();
Class<?> clazz = loader.loadClass("DynamicClass" + System.nanoTime());
classList.add(clazz);
System.out.println("Loaded: " + classList.size() + " classes");
}
} catch (OutOfMemoryError e) {
System.err.println("Metaspace OutOfMemoryError: " + e.getMessage());
e.printStackTrace();
}
}
}
// Запуск с ограничением Metaspace:
// java -XX:MetaspaceSize=50m -XX:MaxMetaspaceSize=100m MetaspaceOutOfMemoryExample
3. Stack space — переполнение стека (Stack Overflow)
Отличается от heap OOM, но приводит к аналогичной ошибке:
public class StackOutOfMemoryExample {
private static int depth = 0;
public static void recursiveMethod() {
depth++;
if (depth % 10000 == 0) {
System.out.println("Recursion depth: " + depth);
}
recursiveMethod(); // Бесконечная рекурсия
}
public static void main(String[] args) {
try {
recursiveMethod();
} catch (StackOverflowError e) {
System.err.println("Stack Overflow at depth: " + depth);
e.printStackTrace();
}
}
}
// Запуск с ограничением стека:
// java -Xss256k StackOutOfMemoryExample
4. Direct buffer memory — нехватка памяти для DirectBuffer
import java.nio.ByteBuffer;
public class DirectBufferOutOfMemoryExample {
public static void main(String[] args) {
try {
while (true) {
// Создание DirectBuffer'ов
ByteBuffer buffer = ByteBuffer.allocateDirect(1024 * 1024 * 10); // 10 MB
System.out.println("Allocated direct buffer");
}
} catch (OutOfMemoryError e) {
System.err.println("Direct buffer OutOfMemoryError: " + e.getMessage());
e.printStackTrace();
}
}
}
// Запуск с ограничением direct memory:
// java -XX:MaxDirectMemorySize=256m DirectBufferOutOfMemoryExample
Диагностика OutOfMemoryError
1. Heap Dump анализ
# Генерация heap dump при OOM
java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heap.hprof MyApp
# Или через jmap
jmap -dump:live,format=b,file=heap.bin <pid>
# Анализ dump файла (например, Eclipse MAT или jhat)
jhat heap.bin
2. Мониторинг памяти во время выполнения
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
public class MemoryMonitor {
public static void main(String[] args) throws InterruptedException {
MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
for (int i = 0; i < 10; i++) {
MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
MemoryUsage nonHeapUsage = memoryBean.getNonHeapMemoryUsage();
System.out.println("Heap Memory:");
System.out.println(" Used: " + heapUsage.getUsed() / 1024 / 1024 + " MB");
System.out.println(" Max: " + heapUsage.getMax() / 1024 / 1024 + " MB");
System.out.println(" Committed: " + heapUsage.getCommitted() / 1024 / 1024 + " MB");
System.out.println("Non-Heap Memory:");
System.out.println(" Used: " + nonHeapUsage.getUsed() / 1024 / 1024 + " MB");
System.out.println(" Max: " + nonHeapUsage.getMax() / 1024 / 1024 + " MB");
Thread.sleep(1000);
}
}
}
3. JVM опции для диагностики
# Подробные логи сборки мусора
java -Xmx256m -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps \
-Xloggc:gc.log MyApp
# Trace освобождения памяти
java -XX:+TraceClassLoading -XX:+TraceClassUnloading MyApp
# Jstack для анализа потоков (при зависании)
jstack <pid> > threads.txt
Причины OutOfMemoryError
1. Утечки памяти (Memory Leaks)
public class MemoryLeakExample {
private static List<Object> cache = new ArrayList<>();
public static void addToCache(Object obj) {
cache.add(obj); // Объекты никогда не удаляются!
}
// ПРАВИЛЬНО: использовать WeakHashMap
private static Map<Object, Object> weakCache = new WeakHashMap<>();
public static void addToWeakCache(Object key, Object value) {
weakCache.put(key, value);
// Значение удалится из памяти если на key нет больше ссылок
}
}
2. Большие объекты и collections
public class LargeObjectExample {
// ПРОБЛЕМА: загрузка большого файла в памяти
public void loadLargeFile(String filepath) throws Exception {
byte[] data = Files.readAllBytes(Paths.get(filepath)); // Может быть 1GB+
}
// РЕШЕНИЕ: потоковое чтение
public void readLargeFileStreaming(String filepath) throws Exception {
try (BufferedReader reader = new BufferedReader(
new FileReader(filepath), 8192)) {
String line;
while ((line = reader.readLine()) != null) {
processLine(line);
}
}
}
}
3. Циклические ссылки и сборка мусора
public class CircularReferenceExample {
public static class Node {
Node next;
byte[] data = new byte[1024 * 1024]; // 1 MB
}
public static void main(String[] args) {
// Создание циклических ссылок
Node head = new Node();
Node current = head;
for (int i = 0; i < 100; i++) {
Node next = new Node();
current.next = next;
current = next;
}
current.next = head; // Цикл!
// Сборщик мусора сможет очистить это
}
}
Стратегии предотвращения OutOfMemoryError
1. Правильная настройка JVM параметров
# Минимальное и максимальное значение heap
java -Xms1024m -Xmx2048m MyApp
# Размер молодого поколения
java -Xms1024m -Xmx2048m -Xmn256m MyApp
# Использование G1GC для лучшей работы с памятью
java -XX:+UseG1GC -Xmx4g MyApp
2. Использование Stream API вместо Collections
// НЕПРАВИЛЬНО: загружает всё в память
List<Integer> allNumbers = file.lines()
.map(Integer::parseInt)
.collect(Collectors.toList()); // Весь файл в памяти!
// ПРАВИЛЬНО: потоком
file.lines()
.map(Integer::parseInt)
.forEach(this::processNumber); // По одному элементу
3. Освобождение ресурсов
public class ResourceManagementExample {
public void processData() {
try (BufferedReader reader = new BufferedReader(
new FileReader("data.txt"))) {
// reader будет закрыт автоматически
String line;
while ((line = reader.readLine()) != null) {
processLine(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Заключение
OutOfMemoryError возникает когда JVM не может выделить память для объектов. Основные подходы к решению:
- Настроить JVM параметры (Xmx, Xms) в соответствии с требованиями
- Находить и исправлять утечки памяти с помощью heap dump анализа
- Использовать потоковые операции вместо загрузки всего в памяти
- Мониторить использование памяти в production
- Использовать современные GC алгоритмы (G1GC, ZGC)
- Правильно управлять жизненным циклом объектов