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

Как устроена область памяти 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 паузы через правильную конфигурацию и управление памятью.