← Назад к вопросам
Как устроена область памяти heap?
2.0 Middle🔥 241 комментариев
#JVM и управление памятью#Многопоточность#Основы Java
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как устроена область памяти Heap
Heap — это основная область памяти JVM, где хранятся все объекты и массивы. Понимание структуры heap критично для оптимизации производительности и предотвращения утечек памяти.
1. Структура Heap в современных JVM
Heap делится на несколько поколений (generations):
┌────────────────────────────────────────┐
│ HEAP Memory │
├────────────────────────────────────────┤
│ │
│ Young Generation (около 30% heap) │
│ ┌──────────────────────────────────┐ │
│ │ Eden Space (большинство новых) │ │
│ │ ┌────────┬────────┬────────┐ │ │
│ │ │ obj1 │ obj2 │ obj3 │ │ │
│ │ └────────┴────────┴────────┘ │ │
│ └──────────────────────────────────┘ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Survivor 0 │ │ Survivor 1 │ │
│ │ (живые нов) │ │ (живые нов) │ │
│ └──────────────┘ └──────────────┘ │
│ │
├────────────────────────────────────────┤
│ │
│ Old Generation (около 70% heap) │
│ ┌──────────────────────────────────┐ │
│ │ Долгоживущие объекты │ │
│ │ ┌──────────────────────────────┐ │ │
│ │ │ obj_survived_15_gc_events │ │ │
│ │ │ long-term data │ │ │
│ │ └──────────────────────────────┘ │ │
│ └──────────────────────────────────┘ │
│ │
└────────────────────────────────────────┘
2. Young Generation - место рождения объектов
Young Generation содержит новосозданные объекты и подвергается частому GC:
public class YoungGenerationDemo {
public static void demonstrateYoungGen() {
// Все эти объекты создаются в Young Generation
// 1. Eden Space - место, где рождаются объекты
List<String> tempList = new ArrayList<>(); // На Eden
for (int i = 0; i < 1000; i++) {
String temp = "Object " + i; // На Eden
tempList.add(temp);
}
// 2. Minor GC (частый)
// - JVM периодически сканирует Eden
// - Объекты, на которые нет references, удаляются (fast collection)
// - Живые объекты перемещаются в Survivor пространство
System.gc(); // Может запустить Minor GC
}
}
// Жизненный цикл в Young Generation:
//
// Шаг 1: Создание
// Object1 → EDEN (возраст 0)
//
// Шаг 2: Первый Minor GC
// Object1 → SURVIVOR 0 (возраст 1)
// Object2 → EDEN (новый объект)
//
// Шаг 3: Второй Minor GC
// Object1 → SURVIVOR 1 (возраст 2)
// Object2 → SURVIVOR 0 (возраст 1)
//
// Шаг 4: После 15 Minor GC (настраивается)
// Object1 → OLD GENERATION (возраст 15 → promoted)
3. Old Generation - долгоживущие объекты
Old Generation содержит объекты, пережившие много Minor GC:
public class OldGenerationDemo {
// Статический reference - ОЧЕНЬ долгоживущий
private static List<CacheEntry> cache = new ArrayList<>();
public static void cacheData() {
// Эти объекты попадут в Old Generation
for (int i = 0; i < 10_000; i++) {
cache.add(new CacheEntry(i, "Data " + i));
}
// Процесс:
// 1. Все объекты рождаются в Eden
// 2. После многих Minor GC переживают в Old Generation
// 3. Full GC происходит редко (старается избежать паузы)
// 4. Если достигнут limit → OutOfMemoryError
}
// Пример утечки памяти в Old Generation:
public static void memoryLeak() {
for (int i = 0; i < 1_000_000; i++) {
cache.add(new CacheEntry(i, "Data " + i));
// Кэш растёт без предела!
// Объекты в Old Generation никогда не удаляются
// Рано или поздно → OutOfMemoryError: Java heap space
}
}
}
4. Процесс Garbage Collection
Minor GC (частый, быстрый)
Шаг 1: До GC
┌──────────────────────────────────┐
│ Young Generation │
│ ┌────────────────────────────┐ │
│ │ Eden: [obj1, obj2, obj3] │ │ ← молодые объекты
│ │ Survivor0: [obj4] │ │ ← выжившие
│ │ Survivor1: [] │ │
│ └────────────────────────────┘ │
└──────────────────────────────────┘
Шаг 2: Minor GC запустился
1. Сканирует Eden и Survivor0
2. Отмечает живые объекты (есть roots references)
3. Копирует живые в Survivor1
4. Удаляет мертвые (быстро)
Шаг 3: После GC
┌──────────────────────────────────┐
│ Young Generation │
│ ┌────────────────────────────┐ │
│ │ Eden: [] │ │ ← очищено
│ │ Survivor0: [] │ │
│ │ Survivor1: [obj1, obj4] │ │ ← выжившие
│ └────────────────────────────┘ │
└──────────────────────────────────┘
Minor GC занимает: миллисекунды
Full GC (редкий, медленный)
Когда Minor GC не достаточно → Full GC:
1. Сканирует весь Young Generation
2. Сканирует весь Old Generation
3. Компактирует оставшиеся объекты
4. Вызывает большую паузу приложения
Full GC занимает: СЕКУНДЫ (плохо для production)
5. Практический пример: Анализ использования Heap
public class HeapAnalysis {
static class User {
String name;
int age;
List<String> emails;
public User(String name, int age) {
this.name = name;
this.age = age;
this.emails = new ArrayList<>();
}
}
public static void main(String[] args) {
// Созданием миллиона объектов
List<User> users = new ArrayList<>();
// Статистика памяти
Runtime rt = Runtime.getRuntime();
long memBefore = rt.totalMemory() - rt.freeMemory();
// Создание объектов → Eden Space
for (int i = 0; i < 1_000_000; i++) {
users.add(new User("User" + i, 20 + (i % 50)));
}
long memAfter = rt.totalMemory() - rt.freeMemory();
System.out.println("Memory used: " + (memAfter - memBefore) / 1024 / 1024 + " MB");
// Путь в памяти:
// 1. ArrayList создан в Eden
// 2. 1M User объектов созданы в Eden
// 3. Все строки ("User0", "User1", ...) в Eden
// 4. После минимум 1 Minor GC → переместятся в Survivor
// 5. После минимум 15 Minor GC → переместятся в Old Gen
// GC Events:
// Minor GC: каждые ~500MB в Eden
// Full GC: если Old Gen переполнен
}
}
6. Конфигурация Heap размера
# Основные параметры JVM
# Минимальный размер heap (обычно выделяется при старте)
-Xms512m
# Максимальный размер heap
-Xmx2g
# Размер Young Generation
-XX:NewSize=256m
-XX:MaxNewSize=256m
# Ratio: Old / Young
-XX:OldSize=1g
# Или как процент от полного heap
-XX:NewRatio=3 (значит Old в 3 раза больше Young)
# Пример: запуск приложения
java -Xms1g -Xmx4g -XX:NewRatio=3 -jar myapp.jar
7. Мониторирование Heap
import java.lang.management.*;
public class HeapMonitoring {
public static void printHeapInfo() {
MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
System.out.println("=== Heap Memory ===");
System.out.println("Init: " + heapUsage.getInit() / 1024 / 1024 + " MB");
System.out.println("Used: " + heapUsage.getUsed() / 1024 / 1024 + " MB");
System.out.println("Committed: " + heapUsage.getCommitted() / 1024 / 1024 + " MB");
System.out.println("Max: " + heapUsage.getMax() / 1024 / 1024 + " MB");
// Процент использования
double percent = (double) heapUsage.getUsed() / heapUsage.getMax() * 100;
System.out.println("Usage: " + String.format("%.1f%%", percent));
}
public static void printGenerationInfo() {
for (MemoryPoolMXBean pool : ManagementFactory.getMemoryPoolMXBeans()) {
if (pool.getType().equals(MemoryType.HEAP)) {
MemoryUsage usage = pool.getUsage();
System.out.println(pool.getName() + ": " + usage.getUsed() / 1024 / 1024 + " MB");
}
}
}
public static void main(String[] args) {
printHeapInfo();
System.out.println();
printGenerationInfo();
}
}
8. Проблемы и решения
Проблема 1: OutOfMemoryError - Heap space
// ПРОБЛЕМА: утечка памяти
static Map<String, Data> cache = new HashMap<>();
public void leak() {
for (int i = 0; i < Integer.MAX_VALUE; i++) {
cache.put("key" + i, new Data()); // Растёт бесконечно!
}
}
// РЕШЕНИЕ: используйте WeakHashMap или кэш с limit
import java.util.WeakHashMap;
public void fixed() {
Map<String, Data> cache = new WeakHashMap<>();
// Объекты удаляются, если на них нет других references
}
// ИЛИ: кэш с максимальным размером
import com.google.common.cache.CacheBuilder;
LoadingCache<String, Data> cache = CacheBuilder.newBuilder()
.maximumSize(10000)
.expireAfterWrite(1, TimeUnit.HOURS)
.build(key -> new Data());
Проблема 2: Full GC паузы
// ПРОБЛЕМА: большая пауза при Full GC
public void longRunningOperation() {
List<byte[]> largeObjects = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
largeObjects.add(new byte[1024 * 1024 * 5]); // 5 MB каждый
}
// После использования → Full GC (паузы 5+ секунд!)
// Использование...
}
// РЕШЕНИЕ: использовать G1GC или другой garbage collector
// java -XX:+UseG1GC -jar myapp.jar
9. GC Collectors и оптимизация
# Различные Garbage Collectors:
# 1. Serial GC (для малых приложений)
-XX:+UseSerialGC
# 2. Parallel GC (multi-threaded, по умолчанию)
-XX:+UseParallelGC
# 3. CMS GC (low-latency, deprecated)
-XX:+UseConcMarkSweepGC
# 4. G1GC (современный, рекомендуемый)
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
# Пример оптимизированная конфигурация:
java -Xms4g -Xmx4g \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=200 \
-XX:+ParallelRefProcEnabled \
-XX:+UnlockDiagnosticVMOptions \
-XX:G1SummarizeRSetStatsPeriod=1 \
-jar application.jar
Резюме: Структура Heap
| Область | Описание | GC Частота | Время |
|---|---|---|---|
| Eden | Новые объекты | Частая | Миллисекунды |
| Survivor 0/1 | Молодые выжившие | Частая | Миллисекунды |
| Old Generation | Долгоживущие | Редкая | Секунды |
Золотое правило: Минимизируйте Full GC паузы через правильную конфигурацию и управление памятью.