← Назад к вопросам

Как можно активировать 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 не может выделить память для объектов. Основные подходы к решению:

  1. Настроить JVM параметры (Xmx, Xms) в соответствии с требованиями
  2. Находить и исправлять утечки памяти с помощью heap dump анализа
  3. Использовать потоковые операции вместо загрузки всего в памяти
  4. Мониторить использование памяти в production
  5. Использовать современные GC алгоритмы (G1GC, ZGC)
  6. Правильно управлять жизненным циклом объектов