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

Что произойдет если переполнится куча?

1.3 Junior🔥 141 комментариев
#JVM и управление памятью

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Переполнение кучи (Heap Overflow) в Java

Основной ответ

Если в Java переполнится куча (heap), будет выброшено исключение OutOfMemoryError (OOM). Это исключение наследуется от Error, а не Exception, что означает, что его невозможно "поймать" обычным способом.

Что такое куча (Heap)

Куча — это область памяти в JVM, где хранятся все объекты. Размер кучи ограничен и определяется параметром -Xmx при запуске JVM:

# Запуск с максимальным размером кучи 512 МБ
java -Xmx512m MyApplication

# Или в gradle/maven конфигурации
JAVA_OPTS="-Xms256m -Xmx1024m"

Пример переполнения кучи

public class HeapOverflowExample {
    public static void main(String[] args) {
        List<byte[]> list = new ArrayList<>();
        
        try {
            int counter = 0;
            while (true) {
                // Создаём массив в 1 МБ и добавляем в список
                list.add(new byte[1024 * 1024]);
                counter++;
                System.out.println("Выделено объектов: " + counter);
            }
        } catch (OutOfMemoryError e) {
            System.out.println("OutOfMemoryError: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

При запуске с java -Xmx256m HeapOverflowExample приложение упадёт с:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

Типы OutOfMemoryError

1. "Java heap space" — обычное переполнение кучи:

public class HeapSpaceOOM {
    public static void main(String[] args) {
        List<String> strings = new ArrayList<>();
        while (true) {
            strings.add(new String(new char[1000000])); // 1 МБ строка
        }
        // Exception: java.lang.OutOfMemoryError: Java heap space
    }
}

2. "PermGen space" / "Metaspace" — переполнение метаданных класса:

public class MetaspaceOOM {
    static List<Class<?>> classes = new ArrayList<>();
    
    public static void main(String[] args) {
        try {
            int i = 0;
            while (true) {
                // Динамическое создание классов
                Class<?> clazz = createDynamicClass("Class" + (i++));
                classes.add(clazz);
            }
        } catch (OutOfMemoryError e) {
            System.out.println("OOM Metaspace: " + e.getMessage());
            // Exception: java.lang.OutOfMemoryError: Metaspace
        }
    }
    
    private static Class<?> createDynamicClass(String name) {
        // Создание класса через bytecode manipulation
        return null;
    }
}

3. "GC overhead limit exceeded" — сборщик мусора не может очистить память:

Exception: java.lang.OutOfMemoryError: GC overhead limit exceeded

Это происходит, когда GC потратил более 98% времени на очистку,
но освободил менее 2% памяти.

4. "Unable to create new native thread" — недостаточно системной памяти:

public class ThreadOOM {
    public static void main(String[] args) {
        try {
            while (true) {
                new Thread(() -> {
                    try { Thread.sleep(10000); } catch (Exception e) {}
                }).start();
            }
        } catch (OutOfMemoryError e) {
            System.out.println("Unable to create native thread: " + e.getMessage());
        }
    }
}

Что происходит при OutOfMemoryError

  1. JVM выбрасывает исключение OutOfMemoryError
  2. Текущий поток падает (но не обязательно всё приложение)
  3. Сборщик мусора не может помочь (нет большой свободной памяти)
  4. Приложение может быть нестабильным после этого
  5. Ресурсы могут остаться в несогласованном состоянии

Почему нельзя "поймать" OutOfMemoryError

public class WhyCantCatchOOM {
    public static void main(String[] args) {
        try {
            List<byte[]> list = new ArrayList<>();
            while (true) {
                list.add(new byte[1024 * 1024]);
            }
        } catch (OutOfMemoryError e) {
            // Даже если мы его "поймаем", мы не в состоянии ничего сделать
            // Память полностью исчерпана, даже try-catch требует памяти!
            
            // Логирование может не сработать (недостаточно памяти)
            System.out.println("OOM occurred: " + e);
            
            // Очистка может не помочь
            list = null;
            System.gc(); // Обычно это не спасает
            
            // Выброс нового исключения потребует ещё памяти
            throw new RuntimeException("OOM caught");
        }
    }
}

Профилактика и диагностика

1. Настройка параметров JVM:

# Начальный и максимальный размер кучи
java -Xms1024m -Xmx1024m MyApp

# Логирование GC
java -Xmx512m -XX:+PrintGCDetails -XX:+PrintGCDateStamps MyApp

# Dump heap при OOM
java -Xmx512m -XX:+HeapDumpOnOutOfMemoryError MyApp

2. Анализ heap dump:

# Создание dump вручную
jmap -dump:live,format=b,file=heap.dump <pid>

# Анализ в Eclipse MAT (Memory Analyzer Tool)
# или JProfiler

3. Поиск утечек памяти в коде:

public class MemoryLeak {
    static List<String> cache = new ArrayList<>(); // Утечка!
    
    public void addToCache(String value) {
        cache.add(value); // Растёт бесконечно
    }
}

// Лучше
public class NoMemoryLeak {
    private static final int MAX_CACHE_SIZE = 1000;
    private final LRUCache<String, String> cache;
    
    public NoMemoryLeak() {
        cache = new LRUCache<>(MAX_CACHE_SIZE);
    }
}

Мониторинг памяти в production

import java.lang.management.MemoryMXBean;
import java.lang.management.ManagementFactory;

public class HeapMonitor {
    public static void main(String[] args) {
        MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
        
        long heapUsed = memoryMXBean.getHeapMemoryUsage().getUsed();
        long heapMax = memoryMXBean.getHeapMemoryUsage().getMax();
        
        double percentUsed = (double) heapUsed / heapMax * 100;
        System.out.println("Heap usage: " + percentUsed + "%");
        
        if (percentUsed > 90) {
            System.out.println("WARNING: High memory usage!");
            // Отправить алерт
        }
    }
}

Лучшие практики

  • Правильно установите -Xmx (не слишком мало, не слишком много)
  • Избегайте утечек памяти: закрывайте ресурсы, удаляйте ссылки
  • Используйте weak/soft references для кэшей
  • Мониторьте память в production
  • Анализируйте heap dumps при возникновении проблем
  • Используйте профайлеры во время разработки
  • Не ловите OutOfMemoryError — это неприёмлемо