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

На какие области делится Heap

2.0 Middle🔥 171 комментариев
#Другое

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

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

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

На какие области делится Heap

Краткий ответ

Heap (кучу памяти) делится на несколько областей для оптимизации работы Garbage Collector: Young Generation (молодое поколение), Old Generation (старое поколение) и Metaspace (метапространство). Такое разделение основано на гипотезе поколений (Generational Hypothesis), которая гласит, что большинство объектов живут недолго.

Структура Heap в JVM

┌──────────────────────────────────────────────────────┐
│                    TOTAL HEAP MEMORY                 │
├──────────────────────────────────────────────────────┤
│                                                      │
│  Young Generation (Молодое поколение)                │
│  ├─ Eden Space          (большинство новых объектов) │
│  ├─ Survivor 0 (S0)     (пережившие одну GC)        │
│  └─ Survivor 1 (S1)     (пережившие одну GC)        │
│                                                      │
│  Old Generation (Старое поколение)                   │
│  └─ (объекты, пережившие несколько GC)              │
│                                                      │
└──────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────┐
│                    METASPACE (Java 8+)               │
│  (классы, методы, метаданные, native memory)        │
└──────────────────────────────────────────────────────┘

1. Young Generation (молодое поколение)

Описание: Где создаются и быстро удаляются большинство объектов

Young Generation
├─ Eden Space          (по умолчанию ~80% от Young Gen)
├─ Survivor Space 0    (по умолчанию ~10% от Young Gen)
└─ Survivor Space 1    (по умолчанию ~10% от Young Gen)

Жизненный цикл объекта в Young Generation:

public class YoungGenerationExample {
    public static void main(String[] args) {
        // Объект создаётся в Eden Space
        Person person1 = new Person("Alice");
        
        // Во время Minor GC:
        // 1. Живые объекты копируются из Eden в Survivor 0 (S0)
        // 2. Объекты S0 копируются в Survivor 1 (S1)
        // 3. Счётчик поколений увеличивается (age)
        
        // После нескольких Minor GC объекты переходят в Old Generation
    }
}

// Возрастное пороговое значение (по умолчанию 15)
// После достижения этого значения объект переходит в Old Generation

Параметры JVM для Young Generation:

# Размер Young Generation
-Xmn512m

# Соотношение Eden и Survivor
-XX:SurvivorRatio=8    # Eden:Survivor = 8:1 (из 10 частей: 8 Eden, 1 S0, 1 S1)

# Инициальное и максимальное значение
-XX:NewSize=256m
-XX:MaxNewSize=512m

2. Old Generation (старое поколение)

Описание: Долгоживущие объекты, которые пережили несколько Minor GC

public class OldGenerationExample {
    // Долгоживущие объекты
    private static final List<Data> cache = new ArrayList<>();
    
    public static void main(String[] args) {
        // Объекты для кэша попадают в Old Generation
        for (int i = 0; i < 1000000; i++) {
            cache.add(new Data(i));
        }
        
        // Эти объекты будут жить долго
        // Major GC (Full GC) удаляет мёртвые объекты отсюда
    }
    
    static class Data {
        int value;
        Data(int value) { this.value = value; }
    }
}

GC в Old Generation:

Major GC / Full GC
- Serial GC         (однопоточный)
- Parallel GC       (многопоточный)
- CMS GC            (低停顿)
- G1GC              (современный)
- ZGC               (минимальные паузы)

3. Metaspace (Java 8+)

Описание: Хранит метаданные классов, методов, констант (native memory, не в Heap)

public class MetaspaceExample {
    // Классы, методы, байт-коды хранятся в Metaspace
    public void method1() { }
    public void method2() { }
    public void method3() { }
    
    static class InnerClass {
        // Каждый новый класс — новые записи в Metaspace
    }
}

// Dynamic class loading (например, в фреймворках)
public class DynamicClassLoading {
    public static void main(String[] args) throws Exception {
        // Каждый раз при создании нового класса растёт Metaspace
        for (int i = 0; i < 100000; i++) {
            Class<?> clazz = Class.forName("com.example.Class" + i);
        }
    }
}

Параметры JVM для Metaspace:

# Начальный размер Metaspace
-XX:MetaspaceSize=64m

# Максимальный размер Metaspace
-XX:MaxMetaspaceSize=256m

# Если Metaspace переполнится — OutOfMemoryError: Metaspace

Полная таблица памяти JVM

┌─────────────────────────────────────────────────────────┐
│              RUNTIME DATA AREAS                         │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  HEAP (распределяется между потоками)                   │
│  ├─ Young Generation                                    │
│  │  ├─ Eden Space                                       │
│  │  ├─ Survivor Space 0                                 │
│  │  └─ Survivor Space 1                                 │
│  └─ Old Generation                                      │
│                                                         │
│  NON-HEAP (не распределяется между потоками)            │
│  ├─ Metaspace       (классы, методы)                    │
│  ├─ Code Cache      (скомпилированный код JIT)         │
│  └─ Compressed Class Space (Java 8+)                    │
│                                                         │
│  STACK (по одному на каждый поток)                      │
│  ├─ локальные переменные                                │
│  ├─ ссылки на объекты                                   │
│  └─ вызовы методов                                      │
│                                                         │
└─────────────────────────────────────────────────────────┘

Отслеживание использования памяти

public class MemoryMonitoring {
    public static void main(String[] args) {
        Runtime runtime = Runtime.getRuntime();
        MemoryMXBean memBean = ManagementFactory.getMemoryMXBean();
        
        // Heap Memory
        MemoryUsage heapUsage = memBean.getHeapMemoryUsage();
        System.out.println("Heap Init: " + heapUsage.getInit());
        System.out.println("Heap Used: " + heapUsage.getUsed());
        System.out.println("Heap Committed: " + heapUsage.getCommitted());
        System.out.println("Heap Max: " + heapUsage.getMax());
        
        // Non-Heap Memory (Metaspace)
        MemoryUsage nonHeapUsage = memBean.getNonHeapMemoryUsage();
        System.out.println("\nNon-Heap Used: " + nonHeapUsage.getUsed());
        System.out.println("Non-Heap Max: " + nonHeapUsage.getMax());
        
        // Детальная информация по типам памяти
        for (MemoryPoolMXBean pool : ManagementFactory.getMemoryPoolMXBeans()) {
            MemoryUsage usage = pool.getUsage();
            System.out.println("\n" + pool.getName());
            System.out.println("  Type: " + pool.getType());
            System.out.println("  Used: " + usage.getUsed());
            System.out.println("  Max: " + usage.getMax());
        }
    }
}

// Вывод:
// Heap Init: 268435456
// Heap Used: 125829120
// Heap Committed: 268435456
// Heap Max: 2147483648
//
// Non-Heap Used: 34603008
// Non-Heap Max: -1
//
// PS Scavenge
//   Type: YOUNG_GEN
//   Used: 95814656
//   Max: 357368832
//
// PS MarkSweep
//   Type: OLD_GEN
//   Used: 30014464
//   Max: 1431633920
//
// Code Cache
//   Type: NON_HEAP
//   Used: 12582912
//   Max: 251658240

Гипотеза поколений (Generational Hypothesis)

Почему Heap разделён на поколения:

public class GenerationalExample {
    public static void main(String[] args) {
        // Объект 1: Живёт очень недолго
        for (int i = 0; i < 1000000; i++) {
            String temp = "temporary_" + i;  // Создаётся и удаляется быстро
        }
        
        // Объект 2: Живёт долго
        List<String> cache = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            cache.add("cached_" + i);  // Останется на протяжении всей программы
        }
        
        // Эмпирические наблюдения показывают:
        // 1. Молодые объекты обычно умирают молодыми (90%+ в Young Gen)
        // 2. Старые объекты живут долго (меньше GC нужно)
        // 3. Раздельная GC для Young и Old более эффективна
    }
}

Сравнение GC операций

ПараметрMinor GCMajor GCFull GC
ОбластьYoung GenOld GenEntire Heap
ЧастотаЧастоРедкоОчень редко
ДлительностьБыстрыйМедленнееОчень медленно
Стоп-the-world10-100ms100ms-1s1s+
АлгоритмCopyMark-SweepMark-Sweep-Compact

Пример JVM параметров для оптимизации

# Начальный размер Heap
-Xms2g

# Максимальный размер Heap
-Xmx4g

# Размер Young Generation
-Xmn1g

# Соотношение Eden:Survivor
-XX:SurvivorRatio=8

# Порог переноса в Old Generation
-XX:MaxTenuringThreshold=15

# Metaspace
-XX:MetaspaceSize=128m
-XX:MaxMetaspaceSize=512m

# Выбор GC алгоритма (G1GC — рекомендуется для большинства)
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200

Визуализация памяти при работе программы

Время 0 (запуск):
┌─────────────────────┐
│  Eden:    [  ]      │  Young Gen
│  S0:      [  ]      │
│  S1:      [  ]      │
│  Old:     [  ]      │
└─────────────────────┘

Время 1 (после создания объектов):
┌─────────────────────┐
│  Eden:    [████████]│  Young Gen (объекты 1,2,3,4,5)
│  S0:      [  ]      │
│  S1:      [  ]      │
│  Old:     [  ]      │
└─────────────────────┘

Время 2 (Minor GC):
┌─────────────────────┐
│  Eden:    [  ]      │  Young Gen
│  S0:      [████████]│  (живые объекты перемещены в S0)
│  S1:      [  ]      │
│  Old:     [  ]      │
└─────────────────────┘

Время 3 (новые объекты создаются):
┌─────────────────────┐
│  Eden:    [████████]│  Young Gen (новые объекты)
│  S0:      [████████]│  (старые объекты)
│  S1:      [  ]      │
│  Old:     [  ]      │
└─────────────────────┘

Время 4 (Major GC - объект переживает 15 GC):
┌─────────────────────┐
│  Eden:    [  ]      │  Young Gen
│  S0:      [  ]      │
│  S1:      [████████]│  (пережившие объекты, age=15)
│  Old:     [████████]│  (переместились в Old)
└─────────────────────┘

Заключение

Heap разделён на:

  1. Young Generation — для молодых объектов (Eden, S0, S1)

    • Minor GC часто и быстро
    • Объекты живут недолго
  2. Old Generation — для долгоживущих объектов

    • Major GC редко и медленнее
    • Объекты пережили несколько GC
  3. Metaspace — для метаданных классов

    • Native memory
    • Рост при загрузке новых классов

Это разделение позволяет JVM:

  • Оптимизировать GC для разных типов объектов
  • Уменьшить стоп-the-world паузы
  • Улучшить производительность приложения
  • Эффективнее использовать память
На какие области делится Heap | PrepBro