Когда объекты перемещаются между отсеками Heap в JVM?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Перемещение объектов между отсеками Heap
Структура Heap в JVM
Heap разделён на несколько отсеков (generations):
HEAP
┌─────────────────────────────────┐
│ OLD GENERATION (Tenured) │ долгоживущие объекты
├─────────────────────────────────┤
│ SURVIVOR (S0/S1) │ пережившие объекты
├─────────────────────────────────┤
│ YOUNG GENERATION (Eden) │ новые объекты
└─────────────────────────────────┘
1. Young Generation (Молодое поколение)
Все новые объекты создаются в Eden Space:
User user = new User("Alice"); // Создаётся в Eden
Eden характеристики:
- Большая часть Young Generation
- Чистится часто (Minor GC)
- Быстрая очистка
2. Survivor Spaces (S0 и S1)
Объекты, пережившие Minor GC, перемещаются в Survivor Space:
Minor GC процесс:
1. Eden переполнена →
2. Minor GC запускается →
3. Живые объекты перемещаются в S0 →
4. Мёртвые объекты удаляются →
5. Eden очищается
Визуально:
До Minor GC:
Eden [объект1] [объект2] [объект3]
S0 [пусто]
S1 [пусто]
После Minor GC:
Eden [пусто]
S0 [объект1] [объект3] <- пережившие
S1 [пусто]
3. Tenuring (Перемещение в Old Generation)
Объекты переходят в Old Generation после несколько Minor GC.
Каждый объект имеет счётчик возраста (age):
public class HeapMovement {
public static void main(String[] args) {
// Объект создан в Eden
byte[] data = new byte[1024];
// age = 0
// После 1-го Minor GC: age = 1
// После 2-го Minor GC: age = 2
// После 3-го Minor GC: age = 3
// ...
// После N-го Minor GC (tenuring threshold): age >= 15
// → переходит в Old Generation
}
}
Tenuring Threshold (порог возраста):
По умолчанию = 15 (для G1: может быть динамический)
# Установить свой порог
java -XX:InitialTenuringThreshold=5 -XX:MaxTenuringThreshold=10 MyApp
Пример жизненного цикла объекта
public class ObjectLifecycle {
static List<byte[]> objects = new ArrayList<>();
public static void main(String[] args) {
// Объект 1: долгоживущий
byte[] persistent = new byte[1024];
objects.add(persistent);
// persistent переживёт несколько Minor GC
// → в Old Generation
// Объект 2: временный
for (int i = 0; i < 1000; i++) {
byte[] temp = new byte[1024];
// temp удалится в первом же Minor GC
// → никогда не будет в Old Gen
}
}
}
4. Major GC (Full GC)
Отсек Old Generation очищается отдельно, реже:
Minor GC - часто, быстро (Eden, S0, S1)
Major GC - редко, медленно (Old Generation)
Full GC - редко, очень медленно (весь Heap)
Когда происходит Major GC:
- Old Generation переполнена
- Явный вызов
System.gc()(не рекомендуется) - CMS GC рассчитал, что нужна cleanup
// Провоцируем Major GC
public void triggerFullGC() {
byte[] huge = new byte[100 * 1024 * 1024]; // 100MB
// Если недостаточно памяти → Full GC
}
Визуализация жизненного цикла
Объект создан (age=0)
↓
[Eden Space]
↓
Minor GC #1 (age=1)
↓
[Survivor S0]
↓
Minor GC #2 (age=2)
↓
[Survivor S1] ← копируется туда
↓
Minor GC #3 (age=3)
↓
[Survivor S0] ← копируется обратно
↓
... (age повышается)
↓
Minor GC #15 (age=15, >= threshold)
↓
[Old Generation] ← PROMOTION!
↓
Major GC (редко)
↓
УДАЛЕН
Настройка поведения
Для G1 GC (современный):
java -XX:+UseG1GC \
-XX:MaxGCPauseMillis=200 \
-XX:InitialHeapSize=1g \
-XX:MaxHeapSize=4g \
MyApp
Для Parallel GC:
java -XX:+UseParallelGC \
-XX:NewRatio=3 \
-XX:SurvivorRatio=8 \
MyApp
Мониторинг перемещений
Посмотреть age объектов:
java -XX:+PrintTenuringDistribution \
-XX:+PrintGCDetails \
-XX:+PrintGCTimeStamps \
MyApp
Вывод:
Desired survivor size 52428800 bytes, new threshold 6 (max 15)
Age table with threshold 6 (max 15):
age 1: 100000 bytes, 100000 total
age 2: 50000 bytes, 150000 total
age 3: 25000 bytes, 175000 total
age 4: 12000 bytes, 187000 total
age 5: 6000 bytes, 193000 total
age 6: 3000 bytes, 196000 total
196000 → переместятся в Old Gen
Практические примеры
Долгоживущий объект (переходит в Old Gen):
public class Cache {
private static Map<String, Data> cache = new HashMap<>();
public void cacheData(String key, Data value) {
cache.put(key, value); // Останется в памяти долго
// После нескольких Minor GC → Old Gen
}
}
Временные объекты (удаляются быстро):
public void processStream(Stream<String> stream) {
stream
.map(String::toLowerCase) // Временный объект
.filter(s -> s.length() > 5) // Временный объект
.forEach(System.out::println);
// Все временные объекты удалены в Eden
// Никогда не переходят в Old Gen
}
Заключение
Движение объектов в Heap:
- Создание → Eden Space
- Первый Minor GC → Survivor S0 (age=1)
- Второй Minor GC → Survivor S1 (age=2)
- После 15 Minor GC → Old Generation (promotion)
- Major GC → удаление из Old Generation
Это поколение-ориентированный сборщик мусора. Молодые объекты чистятся часто, старые редко. Это оптимально для большинства приложений!