Что произойдет если переполнится куча?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Переполнение кучи (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
- JVM выбрасывает исключение
OutOfMemoryError - Текущий поток падает (но не обязательно всё приложение)
- Сборщик мусора не может помочь (нет большой свободной памяти)
- Приложение может быть нестабильным после этого
- Ресурсы могут остаться в несогласованном состоянии
Почему нельзя "поймать" 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 — это неприёмлемо