İçeriğe geç

Garbage Collection — Üçlü Takas

Uzman 9 dk Sık karşılaşılır

Önce şunu oku: Stack, Heap ve Pass-by-Value

30 saniyede özet

Çöp toplayıcı seçmek bir takas: çok iş, kısa duraklama ve az bellek aynı anda olmaz. En kısa duraklayan toplayıcı en az iş çıkaranıdır; bunu söylemeyen her karşılaştırma eksiktir.

En iyi çöp toplayıcı diye bir şey yok. Doğru soru: neyinden vazgeçmeye razısın?

Altın çizgi uygulamanın işi, kırmızılar donduğu anlar.
Adım adım oku
  1. Parallel toplayıcı seyrek temizler ama temizlerken uygulamayı uzun süre dondurur.
  2. G1 daha sık ama daha kısa duraklar; duraklama süresi bir hedefe göre ayarlanır.
  3. ZGC işinin çoğunu uygulama çalışırken arka planda yapar; duraklamalar çok kısadır.
  4. Bunun bedeli biraz ek CPU'dur. Her toplayıcı başka bir şeyden vazgeçer.
  1. Bayt: Uygulamam her şey yolundayken bir anlığına donuyor. Ne oluyor?

  2. Sen: Belki çöp toplayıcı temizlik yapıyordur?

  3. Bayt: Evet. Asıl soru ne zaman ve ne kadar süre temizlediği.

  4. Bayt: Seyrek ama uzun, sık ama kısa ya da hiç durmadan ama biraz pahalı. Hangisi senin için?

Üçünü birden alamazsın

Üç şey birbiriyle yarışır ve hiçbir toplayıcı üçünü birden kazanmaz:

  • Throughput — CPU’nun ne kadarı uygulamanın işini yapıyor
  • Duraklama — en uzun stop-the-worldToplayıcının çalışabilmesi için tüm uygulama thread'lerinin durdurulduğu aralık. Gecikmeye duyarlı bir serviste p99'u bozan şey budur.Sözlükte gör → süresi
  • Ayak izi — bellek ve CPU ek maliyeti

Bir toplayıcı seçmek, bu üçünden hangisinden vazgeçtiğini seçmektir.

Kafam karıştı, daha basit anlat

Çöp toplayıcı üç şeyi aynı anda en iyi yapamaz: çok iş, kısa duraklama ve az bellek. Birini iyileştirmek, genelde ötekilerden birinden ödünç almaktır.

Hızlı kontrolOrta

"Stop-the-world duraklama" tam olarak neyi durdurur?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Kendin gör

GC seçimi — throughput, duraklama ve ayak izi aynı anda alınamaz

Tohum 5
Parallel GC8 GB heap · 500 MB/sn0 / 600 sn

Young toplama

0236 ms

2458 MB genç kuşak · 4.9 sn’de bir

Full GC

05.73 sn

287 sn'de bir

En uzun duraklama

0.0 ms

Throughput

%93.3

duraklamalar %93.3 · eşzamanlı iş maliyeti düşüldü

Aynı yükte üç toplayıcı

  • Parallel GC5.73 sn%93.3 throughput
  • G1 GC200.0 ms%90.4 throughput
  • ZGC0.5 ms%90.0 throughput
Hız
Adım 0

Şu an ne oldu?

Parallel GC — 0 toplama, en uzun 0 ms

Genç kuşak 4.9 sn’de bir doluyor. Duraklamanın maliyeti çöpe değil, hayatta kalan veriye bağlıdır — kopyalanan şey odur.

Aklında kalsın: Genç toplamanın maliyeti eden’in büyüklüğüyle değil, hayatta kalan nesne miktarıyla orantılıdır. Nesnelerin çoğu genç ölür, bu yüzden genç toplama ucuzdur.

Görevler0/3

  • Bir full GC duraklamasını göraçık

    İpucu

    Parallel GC ile sonuna kadar oynat. Old generation dolduğunda bütün heap’i durdurup toplar.

  • G1’i duraklama hedefinden vazgeçmeye zorlaaçık

    İpucu

    G1 seç, heap’i küçült ve ayırma hızını artır. Eşzamanlı işaretleme yetişemezse G1 full GC’ye düşer.

  • 16+ GB heap ve 1000+ MB/sn ayırmada her duraklamayı 1 ms altında tutaçık

    İpucu

    Bunu yapabilen tek toplayıcı hangisi? Sonra etkili throughput’a bak: düşük duraklamanın bedeli orada.

Olay günlüğü (0)

Henüz olay yok. Oynat veya adımla.

Alttaki karşılaştırma tablosuna bak — sıralama sütuna göre değişiyor:

  1. Parallel, 8 GB, 500 MB/sn. En uzun duraklama 5.7 saniye. Ama throughputBirim zamanda yapılan iş miktarı. GC bağlamında: CPU'nun ne kadarı uygulamanın işini yapıyor, ne kadarı toplayıcıya gidiyor.Sözlükte gör → en yüksek: %93.3.
  2. ZGC’ye geç. Duraklama 0.5 ms. Throughput ise %90 — daha düşük.
  3. Heap’i 32 GB yap, Parallele dön. Duraklama 5.7 sn’den 22.9 sn’ye çıktı.
  4. G1, 32 GB. Genç kuşağı 9830 MB’tan 2083 MB’a çekiyor ve 200 ms hedefini tutturuyor.
  5. G1, 2 GB, 2000 MB/sn. Hedef gitti — G1 full GC’ye düştü.
  6. Görevleri tamamla. Sonuncusu bir seçim sorusu: büyük heap ve yoğun ayırmada her duraklamayı 1 ms altında tutan toplayıcı hangisi, bedelini nerede ödüyor?
Kafam karıştı, daha basit anlat

Hangi toplayıcının “en iyi” olduğu, neye baktığına bağlı. Duraklamaya bakarsan biri, toplam işe bakarsan öteki önde çıkar.

Hızlı kontrolİleri

Heap'i 8 GB'tan 32 GB'a çıkardın ve Parallel GC kullanıyorsun. Full GC duraklamalarına ne olur?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Üç toplayıcı, üç takas

Parallel kullanan bir serviste full GC 5 saniye sürüyor. Heap'i 8 GB'tan 32 GB'a çıkarırsan duraklama ne olur? Cevabı göster

Uzar, kısalmaz. Full GC tüm eski kuşağı işaretleyip sıkıştırır, süresi eski kuşağın boyutuyla orantılıdır. Büyük heap full GC’yi yalnızca seyrekleştirir.

2 GB heap → 1.4 sn full GC
8 GB heap → 5.7 sn full GC
32 GB heap → 22.9 sn full GC

Parallel — throughput kralı. Eşzamanlı hiçbir iş yapmaz, uygulamadan CPU çalmaz. Gece çalışan bir toplu iş için doğru tercih, kullanıcıya cevap veren bir servis için felakettir.

G1 — hedefi daha az toplayarak tutturur. -XX:MaxGCPauseMillis=200 verdiğinde genç kuşağı, hayatta kalanları kopyalamak hedefe sığacak kadar küçültür. Ayırma hızı artınca duraklama uzamaz, toplama sıklaşır.

Sınırı şu: promosyon hızı eşzamanlı işaretlemeyi geçerse G1 yetişemez ve full GC’ye düşer. Hedefi çok küçük vermek de işe yaramaz — toplama sıklığı fırlar, toplam maliyet artar.

Aşağıdaki örnek bir bankanın internet şubesinden ve kur ekranından: GC’nin toplayamadığı iki klasik sızıntı ve düzeltmesi, ardından GC’yi ayarlayıp ölçmek için bayraklar ve komutlar. Sayı yok; sayıyı kendi ölçümün verir.

Derinleş · İnternet şubesi ve kur ekranı: canlı nesneler ve ölçüm 4 dosya · ~76 satır · ilk okumada atlayabilirsin
Proje dosyaları

src/main/java/ebank/ SessionRegistry.java Sızıntı 1: internet şubesi oturumlarını tutan sınırsız bir static map. GC ulaşılabilir her nesneyi canlı sayar; bu map her şeye ulaşır.

src/main/java/ebank/SessionRegistry.java
public class SessionRegistry {
// LEAK: every internet-banking session ever created stays reachable from a static
// root, together with the account list and statement pages it loaded.
private static final Map<String, Session> ALL = new ConcurrentHashMap<>();
public static void register(Session session) {
ALL.put(session.id(), session);
}
// FIX: bound it and let entries expire (or use Caffeine with expireAfterAccess).
private static final Map<String, Session> RECENT = Collections.synchronizedMap(
new LinkedHashMap<>(1_024, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<String, Session> eldest) {
return size() > 10_000;
}
});
public static void remember(Session session) {
RECENT.put(session.id(), session);
}
}

src/main/java/ebank/ FxRateTicker.java Sızıntı 2: kur akışına kaydolup hiç ayrılmayan ekran. Dinleyici, etrafındaki bütün nesneleri canlı tutar.

src/main/java/ebank/FxRateTicker.java
// Pushes every EUR/TRY, USD/TRY … tick to the screens that are open right now.
public class FxRateTicker {
private final List<Consumer<Rate>> listeners = new CopyOnWriteArrayList<>();
// Returning a handle makes "unsubscribe" impossible to forget in a try-with-resources.
public AutoCloseable subscribe(Consumer<Rate> listener) {
listeners.add(listener);
return () -> listeners.remove(listener);
}
public void publish(Rate rate) {
listeners.forEach(listener -> listener.accept(rate));
}
}
// A customer's exchange screen, open for a few minutes. Without close(), the ticker keeps
// the lambda, the lambda keeps `this`, and `this` keeps its growing list alive forever.
class ExchangeScreen implements AutoCloseable {
private final AutoCloseable subscription;
private final List<Rate> seen = new ArrayList<>();
ExchangeScreen(FxRateTicker ticker) {
this.subscription = ticker.subscribe(seen::add);
}
@Override
public void close() throws Exception {
subscription.close();
}
}

Dockerfile JVM ayarları: G1, heap'in konteyner belleğine oranı, GC log'u ve bellek dolunca heap dump.

Dockerfile
FROM eclipse-temurin:21-jre
COPY app.jar /app/app.jar
# G1 is the default; the flags below make the choices explicit and observable.
ENV JAVA_TOOL_OPTIONS="-XX:+UseG1GC \
-XX:MaxRAMPercentage=75 \
-XX:MaxGCPauseMillis=200 \
-Xlog:gc*:file=/logs/gc.log:time,uptime,level,tags:filecount=5,filesize=20m \
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/dumps"
ENTRYPOINT ["java", "-jar", "/app/app.jar"]

ops/ measure-gc.sh Ölçüm komutları: heap özeti, canlı sınıf histogramı, JFR kaydı.

ops/measure-gc.sh
PID=$(pgrep -f app.jar)
# Heap regions and occupancy right now
jcmd "$PID" GC.heap_info
# Which classes hold the most live memory (forces a full GC first: not on a busy production node)
jcmd "$PID" GC.class_histogram | head -25
# Two minutes of low-overhead recording: allocation, GC pauses, lock contention
jcmd "$PID" JFR.start name=gc duration=120s filename=/dumps/gc.jfr settings=profile
# Then open /dumps/gc.jfr in JDK Mission Control, and compare with /logs/gc.log.

ZGC — duraklamayı satın alır, throughput ile öder. İşaretleme ve yer değiştirme eşzamanlı olduğu için duraklama heap boyutundan bağımsızdır, 32 GB’ta da milisaniyenin altındadır.

Bedeli: her nesne okuması bir load barrier’dan geçer, düşük ayırma hızında throughput Parallel’den düşüktür. ZGC gecikmeye duyarlı servisler için doğru takastır, toplu işler için yanlış.

Hızlı kontrolİleri

Her iş yükü için hangi toplayıcıyı seçersin?

Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

Sınıflandırılmamış

Throughput öncelikli → Parallel

Duraklama umursanmıyor

    Gecikme öncelikli → ZGC

    Duraklama görünür hâle geliyor

      Çoğu nesne genç ölür

      “Nesnelerin büyük çoğunluğu genç ölür.” Tüm generational GC tasarımı bu gözleme dayanır.

      Genç toplamanın maliyeti çöple değil, hayatta kalanla orantılıdır. Eden’deki 2 GB çöpün hiçbiri yaşamıyorsa toplama neredeyse bedavadır — çöp kopyalanmaz.

      Pratik sonucu: çok sayıda kısa ömürlü nesne üretmek GC için ucuzdur. Pahalı olan, uzun süre hayatta kalanlardır.

      Kafam karıştı, daha basit anlat

      Çoğu nesne doğar, işini görür ve hemen ölür. Toplayıcı yalnızca hâlâ yaşayanları taşır, bu yüzden çok çöp ama az yaşayan varsa toplama neredeyse bedavadır.

      Hızlı kontrolOrta

      Kuşak hipotezi (generational hypothesis) ne diyor ve neden önemli?

      Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

      Tuzaklar

      DurumToplayıcıNeden
      Toplu iş, gece çalışan raporParallelDuraklama umursanmaz, throughput her şeydir
      Tipik web servisi (< 16 GB)G1Varsayılan; makul duraklama, makul throughput
      Düşük gecikmeli servis, büyük heapZGCDuraklama heap’ten bağımsız
      Küçük konteyner, kısa ömürlüSerialEk thread ve ek bellek yok

      Java 9’dan beri varsayılan G1’dir ve çoğu servis için doğru cevap odur. Toplayıcıyı değiştirmeden önce ölç:

      1. Ölç. GC logunu aç (-Xlog:gc*), duraklama dağılımına bak. p99 kaç ms?
      2. Ayırma hızını düşür. Log string’leri, ara koleksiyonlar, kutulama — azaltmak her toplayıcıda kazandırır.
      3. Live set’e bak. Sorun hayatta kalan veri miktarıysa bu bir önbellek ya da sızıntı sorunudur.
      4. Sonra toplayıcı seç.

      Heap’i büyütmek ilk refleks olmamalı: genç toplamanın işini değil sıklığını azaltır, Parallel ve G1’de full GC’yi uzatır.

      Kendini sına

      Şimşek turu1/5

      Parallel toplayıcıda heap'i büyütmek duraklamayı kısaltır.

      Soru 1/3İleri

      Parallel GC kullanan bir uygulamada full GC duraklamaları çok uzun. Heap'i 8 GB'tan 32 GB'a çıkarmak ne yapar?

      Cevabı biliyor musun?Önce birini seç. Tekrar zamanlaması buna göre ayarlanıyor.

      Aklında kalacak üç şey

      1. 1 Çöp toplayıcı seçmek; çok iş, kısa duraklama ve az bellekten hangisinden vazgeçtiğini seçmektir.
      2. 2 Parallel toplayıcıda heap'i büyütmek uzun duraklamayı kısaltmaz, uzatır. Yalnızca daha seyrek yaşanır.
      3. 3 MaxGCPauseMillis bir hedeftir, garanti değil. G1 yetişemediğinde hedefi bırakır ve tam en kötü anda uzun bir duraklama yapar.
      Sonraki kapı Java'da çöp toplayıcı var. O zaman bellek neden yine de sızıyor? Bellek Sızıntısı ve Referans Türleri — GC Neyi Bırakabilir? · 10 dk

      4 kart sonraki derste seni bekliyor

      0/5 kart bu dersten toplandı

      Bu dersin üstüne kurulanlar

      Bunlar bu dersi temel alıyor; hazır olduğunda devam edebilirsin.