İçeriğe geç

Garbage Collector Seçimi ve Akort Edilmesi: G1 GC vs. ZGC

Java uygulamalarında Garbage Collector seçimi yalnızca bellek yönetimiyle ilgili teknik bir ayrıntı değildir. GC; API yanıt sürelerini, işlem hacmini, altyapı maliyetini ve sistemin yoğun trafik altındaki kararlılığını doğrudan etkiler.

Özellikle onlarca veya yüzlerce gigabaytlık heap kullanan sistemlerde ortalama GC süresine bakmak yeterli değildir. Kullanıcı deneyimini ve servis seviyesi hedeflerini çoğunlukla p99, p99.9 ve p99.99 gecikmeleri belirler. Bu nedenle doğru soru “Hangi GC daha hızlı?” değil, “Bu iş yükünün gecikme, throughput ve kaynak tüketimi hedefleri için hangi GC daha uygun?” olmalıdır.

G1 GC, dengeli throughput ve öngörülebilir duraklamalar sağlamayı amaçlayan genel kullanıma yönelik bir collector’dır. ZGC ise daha fazla işi uygulamayla eş zamanlı yürüterek çok büyük heap’lerde dahi ultra düşük duraklama sürelerini hedefler.

Oracle’ın güncel sınıflandırmasına göre G1 “mostly concurrent”, ZGC ise “fully concurrent” bir collector’dır. Oracle, yanıt süresinin en yüksek öncelik olduğu sistemlerde ZGC’yi başlangıç seçeneği olarak önermektedir. Bununla birlikte ZGC’nin düşük duraklama avantajı, iş yüküne bağlı CPU ve bellek maliyetleriyle birlikte değerlendirilmelidir. Oracle Java 25 GC seçim rehberi

Kısa cevap: G1 GC mi, ZGC mi?

Aşağıdaki yaklaşım pratik bir başlangıç noktasıdır:

  • Dengeli throughput, makul bellek tüketimi ve yüzlerce milisaniyenin altında tutulmak istenen GC duraklamaları için G1 GC güçlü bir varsayılan seçimdir.
  • Onlarca ya da yüzlerce gigabaytlık heap, yüksek canlı veri miktarı ve tek haneli milisaniyelik uçtan uca gecikme hedefleri varsa ZGC öncelikli adaydır.
  • CPU kapasitesi sınırlıysa veya en önemli hedef maksimum işlem hacmiyse, ZGC’nin concurrent çalışma maliyeti mutlaka ölçülmelidir.
  • GC değişikliği sentetik mikro benchmark sonucuyla değil, üretime benzeyen yük, veri dağılımı ve allocation profiliyle doğrulanmalıdır.

ZGC düşük gecikme için güçlü bir mimari avantaj sağlar; fakat gerçek zamanlı bir collector değildir ve uygulama için kesin bir SLA garantisi vermez. İşletim sistemi zamanlaması, safepoint işlemleri, JIT derlemesi, disk erişimi, sayfa hataları, kilit rekabeti ve dış servisler de tail latency üretebilir.

G1 GC nasıl çalışır?

Garbage-First Garbage Collector, heap’i eşit büyüklükte bölgelere ayırır. Bu bölgeler sabit biçimde Eden, Survivor veya Old olarak ayrılmış değildir; ihtiyaç oldukça farklı roller üstlenebilir.

G1; generational, bölge tabanlı, evacuating ve çoğunlukla eş zamanlı çalışan bir collector’dır. Global canlılık analizi gibi pahalı işlerin bir bölümünü uygulama thread’leri çalışırken gerçekleştirir. Buna karşılık canlı nesnelerin seçilmiş bölgelerden başka bölgelere kopyalanması ağırlıklı olarak Stop-The-World duraklamaları içinde yapılır.

Oracle’ın tanımına göre G1, belirlenen pause-time hedefine yüksek olasılıkla ulaşmaya çalışır; ancak bu hedef her GC duraklaması için kesin bir üst sınır değildir. Oracle G1 GC mimarisi

G1 GC döngüsü

G1’in çalışma modeli iki ana dönem halinde düşünülebilir.

1. Young-only dönemi

Yeni nesilde biriken nesneler young collection ile toplanır. Yaşamaya devam eden nesneler Survivor bölgelerine veya yaşlarına göre Old Generation’a taşınır.

Young collection sırasında uygulama thread’leri durdurulur. G1, bir sonraki young generation boyutunu geçmiş duraklama sürelerinden ve kopyalanan canlı nesne miktarından yararlanarak hesaplar.

2. Concurrent marking ve space-reclamation dönemi

Old Generation doluluğu Initiating Heap Occupancy eşiğine ulaştığında G1 concurrent marking döngüsünü başlatır:

  1. Bir Concurrent Start young collection yapılır.
  2. Old Generation içindeki erişilebilir nesneler uygulamayla eş zamanlı işaretlenir.
  3. Remark duraklamasında işaretleme sonuçlandırılır.
  4. Cleanup aşamasında bölgelerin canlılık oranları hesaplanır.
  5. Mixed collection’larda young bölgelerle birlikte seçilmiş old bölgeler de boşaltılır.

“Garbage-First” adı, collector’ın önce en fazla çöp içeren ve en verimli biçimde geri kazanılabilecek bölgeleri seçmesinden gelir.

G1’in pause-time modeli

-XX:MaxGCPauseMillis bir zaman sınırı değil, ergonomik bir hedeftir. G1, collection set’e eklenecek bölgelerin sayısını ve young generation büyüklüğünü bu hedefe göre ayarlamaya çalışır.

Örneğin 100 milisaniyelik hedef vermek, hiçbir GC duraklamasının 100 milisaniyeyi geçmeyeceği anlamına gelmez. Çok yüksek canlı nesne oranı, ani allocation artışı, reference processing, remembered set taraması veya humongous object yoğunluğu hedefin aşılmasına neden olabilir.

Remembered set ve card table maliyeti

G1, bölgeler arasındaki referansları bulabilmek için card table ve remembered set yapılarından yararlanır. Uygulama bir referans alanını değiştirdiğinde write barrier ilgili kartı işaretler. Böylece young collection sırasında bütün Old Generation’ın taranması gerekmez.

Bu mekanizma toplama işini sınırlar; ancak uygulama thread’lerine barrier maliyeti, ek metadata tüketimi ve remembered set bakım yükü getirir. Nesne grafiğinde çok yoğun bölge geçişleri bulunan sistemlerde bu maliyet görünür hale gelebilir.

Evacuation ve Full GC riski

G1, canlı nesneleri seçilmiş bölgelerden boş alanlara taşıyarak belleği sıkıştırır. Taşıma için yeterli boş bölge kalmazsa evacuation failure oluşabilir.

Concurrent marking allocation hızına yetişemez ve yeterli alan geri kazanılamazsa G1, son çare olarak Stop-The-World Full GC gerçekleştirebilir. Büyük heap’lerde bu durum saniyelere ulaşabilen ciddi latency sıçramaları oluşturabilir.

Humongous object davranışı

Boyutu bir G1 bölgesinin yarısından büyük olan nesneler “humongous” olarak değerlendirilir ve ardışık bölgelerde tutulur. Büyük byte dizileri, sıkıştırılmış mesajlar, cache blokları ve büyük JSON payload’ları bu kategoriye girebilir.

Humongous allocation:

  • Heap parçalanmasını artırabilir.
  • Concurrent marking döngüsünü erken başlatabilir.
  • Ardışık boş bölge ihtiyacı doğurabilir.
  • Full GC ve hatta OutOfMemoryError riskini yükseltebilir.

GC loglarında humongous region sayısı yüksekse ilk çözüm JVM bayrağı eklemek değil, büyük allocation kaynaklarını uygulama seviyesinde incelemek olmalıdır.

ZGC nasıl çalışır?

ZGC, ölçeklenebilir ve düşük gecikme odaklı, concurrent bir collector’dır. Marking, reference processing ve nesne relocation gibi pahalı işlerin büyük bölümünü uygulama thread’leri çalışırken gerçekleştirir.

Oracle’ın güncel rehberine göre ZGC’nin duraklama süreleri kullanılan heap boyutundan bağımsızdır ve collector birkaç yüz megabayttan 16 TB’a kadar heap’lerde çalışabilir. JDK 25 dokümantasyonu maksimum duraklamaları bir milisaniyenin altında hedefleyen çalışma modelini açıklamaktadır. Oracle ZGC rehberi

Buradaki kritik ifade “heap boyutundan bağımsız” olmasıdır. Bu, heap büyüdükçe concurrent toplam iş miktarının hiç artmadığı anlamına gelmez. Artan canlı veri daha fazla CPU zamanı ve daha uzun concurrent cycle gerektirebilir. Mimari avantaj, pahalı işin Stop-The-World penceresine taşınmamasıdır.

Generational ZGC ve JDK sürümü

ZGC’nin nesilleri kullanan sürümü JDK 21 ile kullanıma sunuldu. Generational ZGC, çoğu nesnenin genç yaşta öldüğü gözleminden yararlanarak young ve old nesilleri farklı sıklıklarda toplar.

Sürüm davranışı önemlidir:

  • JDK 21 ve 22’de generational mod -XX:+ZGenerational ile etkinleştirilir.
  • JDK 23’te generational mod varsayılan hale gelmiştir.
  • JDK 24 itibarıyla non-generational ZGC kaldırılmış ve ZGenerational seçeneğine ihtiyaç kalmamıştır.

Dolayısıyla internetten kopyalanan eski bir JVM başlangıç satırı yeni JDK’da geçersiz olabilir. Bayraklar hedef JDK dağıtımı ve sürümü üzerinde doğrulanmalıdır. Oracle JDK 21 değişiklikleri ve Oracle JDK 24 değişiklikleri

Colored pointers

ZGC’nin temel yapı taşlarından biri colored pointer yaklaşımıdır. Nesne referansları yalnızca bellek adresi taşımaz; marking ve relocation durumuyla ilgili metadata da içerir.

Bu bilgi sayesinde JVM bir referansın:

  • Geçerli GC görünümüne ait olup olmadığını,
  • İşaretleme işlemi gerektirip gerektirmediğini,
  • Nesnenin taşınmış olup olmadığını,
  • Referansın güncellenmesi gerekip gerekmediğini

kontrol edebilir.

Colored pointer yaklaşımı, ZGC’nin nesneleri uygulama thread’leri çalışırken taşıyabilmesinin önemli nedenlerinden biridir.

Load ve store barrier’lar

Uygulama bir nesne referansını okuduğunda veya güncellediğinde JIT tarafından üretilen barrier kodu devreye girebilir. Barrier, referansın GC açısından güvenli ve güncel olmasını sağlar.

Bir nesne concurrent relocation sırasında başka bir konuma taşınmışsa barrier doğru konumu bulabilir ve referansı düzeltebilir. Bu nedenle bütün uygulama thread’lerini durdurup heap genelindeki tüm referansları tek seferde güncellemek gerekmez.

Generational ZGC ayrıca young ve old nesiller arasındaki referansları verimli biçimde izlemek için optimize edilmiş barrier mekanizmaları kullanır. Bunun karşılığı, uygulamanın normal nesne erişim yollarında belirli bir CPU maliyetidir.

Concurrent marking

ZGC, erişilebilir nesneleri uygulama çalışırken işaretler. Kısa Stop-The-World evreleri temel olarak kök durumunu güvenli biçimde yakalamak ve bazı faz geçişlerini koordine etmek için kullanılır.

Duraklamalarda bütün canlı nesne kümesini dolaşmak yerine sınırlı ve heap boyutuna doğrudan bağlı olmayan işler yapılması, large heap sistemlerde ZGC’nin en önemli avantajıdır.

Concurrent relocation

ZGC, seçilen bölgelerdeki canlı nesneleri uygulama çalışırken taşıyabilir. Uygulama eski konumu gösteren bir referansla karşılaştığında barrier mekanizması erişimi doğru adrese yönlendirir.

Böylece:

  • Heap concurrent biçimde sıkıştırılabilir.
  • Uzun bir Stop-The-World compaction aşaması önlenebilir.
  • Büyük canlı veri kümelerinde pause süresinin heap boyutuyla büyümesi engellenebilir.
  • Ayrı ve uzun süren klasik Full GC ihtiyacı ortadan kaldırılabilir.

Large heap sistemlerde ZGC’nin mimari avantajları

1. Duraklama süresi heap boyutuyla ölçeklenmez

G1’de concurrent marking heap genelinde çalışsa da canlı nesnelerin tahliyesi ve collection set işleme gibi önemli işler Stop-The-World aşamalarında yapılır. Heap ve canlı veri büyüdükçe bu aşamaların kuyruk gecikmesine etkisi artabilir.

ZGC’de pahalı marking ve relocation işleri concurrent yürütüldüğü için 32 GB’tan 256 GB’a geçiş, GC pause süresini aynı oranda büyütmez. Büyük in-memory cache, risk hesaplama motoru, fiyatlama sistemi, oyun sunucusu veya gerçek zamanlı analitik platformlarında bu özellik belirleyicidir.

2. Concurrent compaction

Low-pause collector’ların yalnızca erişilemeyen nesneleri bulması yetmez; fragmentation sorununu da uzun duraklama üretmeden çözmesi gerekir. ZGC’nin concurrent relocation yaklaşımı bellek sıkıştırmayı uygulama çalışırken gerçekleştirebilir.

3. Daha düşük tail-latency riski

Ortalama yanıt süresi iyi olsa bile uzun bir Full GC p99.9 değerini bozabilir. ZGC’nin tasarımı bu tür büyük GC kaynaklı sıçramaları azaltmaya yöneliktir.

Bununla birlikte “GC pause düştü” sonucu, uçtan uca latency’nin otomatik olarak düştüğü anlamına gelmez. Concurrent GC thread’leri CPU üzerinde uygulamayla rekabet ederse servis süresi artabilir. Bu nedenle GC pause, CPU utilization ve request latency birlikte izlenmelidir.

4. Allocation spike’larına karşı heap headroom

ZGC concurrent çalıştığı için GC bir cycle’ı tamamlayana kadar uygulama yeni nesne ayırmaya devam eder. Maksimum heap; canlı veri kümesini, cycle boyunca yapılacak allocation’ları ve trafik sıçramalarını karşılayacak boşluğu içermelidir.

Örneğin canlı veri kümesi 40 GB olan bir servise yalnızca 42 GB heap vermek, teorik olarak yeterli görünse de yüksek allocation rate altında allocation stall yaratabilir. ZGC tuning’in en önemli parametresi çoğu zaman karmaşık bir bayrak değil, doğru -Xmx ve yeterli headroom’dur.

5. Adaptif generational yönetim

Modern ZGC, generational yapıda nesil boyutlarını, GC thread sayısını ve tenuring eşiklerini iş yüküne göre dinamik biçimde ayarlar. Bu nedenle başlangıç yaklaşımı az sayıda bayrak kullanmak ve yalnızca ölçüm sonucunda müdahale etmek olmalıdır.

G1 GC ve ZGC çalışma mekanizmalarının karşılaştırması

Teknik özellikG1 GCZGC
Temel hedefThroughput ve makul pause süresi dengesiUltra düşük GC pause ve ölçeklenebilirlik
Çalışma modeliMostly concurrentPahalı GC işlerinde fully concurrent yaklaşım
Heap organizasyonuRegion tabanlı, generationalRegion tabanlı, JDK 24+ yalnızca generational
MarkingBüyük ölçüde concurrent; başlangıç, remark ve cleanup duraklamaları içerirBüyük ölçüde concurrent, kısa faz geçişleri içerir
Nesne taşımaCollection set evacuation işlemi Stop-The-WorldRelocation büyük ölçüde concurrent
Referans takibiCard table, remembered set ve write barrierColored pointers ile load/store barrier’lar
Pause hedefiMaxGCPauseMillis ile ergonomik hedefHeap boyutundan bağımsız, çok kısa pause tasarımı
Full GC riskiAllocation veya evacuation failure durumunda uzun Full GC oluşabilirKlasik uzun Full GC ihtiyacını concurrent relocation ile önlemeyi amaçlar
ThroughputGenellikle daha yüksekConcurrent çalışma ve barrier’lar nedeniyle daha düşük olabilir
CPU gereksinimiDengeli; pause sırasında yüksek paralellikGC cycle boyunca uygulamayla paylaşılacak CPU headroom ister
Heap headroom ihtiyacıOrta; reserve alan yine kritiktirConcurrent cycle sırasında allocation için daha yüksek boşluk gerekebilir
Large heap uygunluğuOnlarca GB ve üzeri heap’lerde kullanılabilirYüzlerce GB ve TB ölçeğinde özellikle güçlüdür
Tuning yaklaşımıPause hedefi, IHOP, reserve ve region davranışı incelenebilirÖncelikle heap boyutu ve allocation headroom ayarlanır
Operasyonel olgunlukUzun süredir varsayılan ve yaygınÜretime hazır; sürüme özgü generational geçişi dikkate alınmalı

Prod ortamında GC seçim kriterleri

KriterG1 GC lehine durumZGC lehine durumÖlçülmesi gereken metrik
Gecikme SLO’su50–300 ms düzeyindeki GC duraklamaları tolere edilebiliyorTek haneli milisaniyelik tail-latency kritikp99, p99.9, maksimum pause
Heap büyüklüğüOrta ve büyük heapÇok büyük heap veya yüksek live-setHeap occupancy, live-set
ThroughputMaksimum işlem hacmi öncelikliBir miktar throughput maliyeti kabul edilebilirİşlem/saniye, CPU/request
Allocation rateDüşük veya ortaYüksek fakat yeterli CPU ve heap headroom mevcutMB/s veya GB/s allocation
CPU kapasitesiCPU limiti sıkıConcurrent GC için rezerv CPU bulunuyorCPU saturation, steal time
Bellek maliyetiSıkı bellek limitiLatency için daha fazla headroom ayrılabilirRSS, committed heap, native memory
Trafik yapısıÖngörülebilir ve dengeliAni allocation artışları yaşanıyorAllocation spike, burst süresi
Büyük nesnelerHumongous allocation düşükBüyük nesneler G1’de fragmentation yaratıyorHumongous region sayısı
Konteyner limitiKüçük ve sıkı pod limitleriBüyük, CPU garantili instance veya podcgroup memory/CPU limitleri
Operasyonel deneyimEkip G1 loglarını ve davranışını iyi tanıyorEkip ZGC benchmark ve kapasite testlerini yapabiliyorIncident ve tuning tecrübesi
JDK sürümüEski veya kurumsal olarak standartlaştırılmış sürümGüncel LTS veya JDK 24+ kullanılabiliyorDağıtım, sürüm ve flag uyumluluğu

Temel JVM bayrakları ve tuning örnekleri

Aşağıdaki değerler doğrudan üretime kopyalanacak reçeteler değildir. Heap büyüklüğü, CPU sayısı, NUMA yapısı, container limitleri ve allocation profili test edilmeden kesinleştirilmemelidir.

G1 GC için güvenli başlangıç profili

java \
  -XX:+UseG1GC \
  -Xms32g \
  -Xmx32g \
  -XX:MaxGCPauseMillis=100 \
  -XX:+AlwaysPreTouch \
  -Xlog:gc*,safepoint:file=/var/log/app/gc.log:time,uptime,level,tags:filecount=10,filesize=100M \
  -jar application.jar

Bayrakların amacı:

  • -XX:+UseG1GC: G1 collector’ı etkinleştirir.
  • -Xms32g -Xmx32g: Heap resizing ve çalışma anındaki sayfa tahsis sürprizlerini azaltır.
  • -XX:MaxGCPauseMillis=100: G1’e 100 ms’lik ergonomik pause hedefi verir.
  • -XX:+AlwaysPreTouch: Heap sayfalarını başlangıçta fiziksel belleğe dokundurur; açılışı uzatabilir fakat runtime latency varyansını azaltabilir.
  • -Xlog:gc*,safepoint: GC ve safepoint davranışını analiz etmeyi sağlar.

G1 için young generation’ı -Xmn ile sabitlemek genellikle önerilmez. G1, pause hedefine ulaşmak için young generation boyutunu adaptif biçimde kullanır; sabit young generation bu kontrol mekanizmasını zayıflatabilir. Oracle’ın önerisi, önce yalnızca heap boyutu ve gerekiyorsa pause hedefiyle başlamaktır. Oracle G1 tuning rehberi

G1’de Full GC riski görülüyorsa

GC loglarında Pause Full (G1 Compaction Pause) veya evacuation failure görülmesi durumunda aşağıdaki seçenekler kontrollü olarak değerlendirilebilir:

-XX:G1ReservePercent=20
-XX:ConcGCThreads=8

G1ReservePercent, allocation ve evacuation için daha fazla güvenlik alanı bırakabilir. ConcGCThreads, marking’in allocation hızına yetişemediği doğrulandıysa artırılabilir. Her ikisi de maliyetsiz değildir: kullanılabilir heap alanını veya uygulamaya kalan CPU’yu azaltabilir.

Adaptive IHOP tahminleri iş yükündeki ani değişikliklere yetişemiyorsa, son çare olarak işaretlemenin daha erken başlatılması denenebilir:

-XX:-G1UseAdaptiveIHOP
-XX:InitiatingHeapOccupancyPercent=30

Bu ayar statik davranış üretir. Sezonsal veya burst karakterli iş yüklerinde adaptif mekanizmadan daha kötü sonuç verebileceği için uzun süreli karşılaştırmalı test gerektirir.

Çok sayıda yinelenen String için

Heap dump ve profiler, yüksek miktarda yinelenen String bulunduğunu gösteriyorsa:

-XX:+UseStringDeduplication

Bu seçenek bellek tüketimini azaltabilir; ancak concurrent deduplication işleminin CPU maliyeti vardır. Kanıt olmadan varsayılan tuning listesine eklenmemelidir.

JDK 24+ ZGC düşük gecikme profili

java \
  -XX:+UseZGC \
  -Xms64g \
  -Xmx64g \
  -XX:+AlwaysPreTouch \
  -Xlog:gc*,safepoint:file=/var/log/app/gc.log:time,uptime,level,tags:filecount=10,filesize=100M \
  -jar application.jar

-Xms ile -Xmx değerlerinin eşitlenmesi:

  • Heap’in çalışma sırasında büyüme ihtiyacını azaltır.
  • ZGC’nin belleği işletim sistemine iade etmesini fiilen sınırlar.
  • Commit/uncommit ve page fault kaynaklı latency varyansını azaltabilir.
  • Başlangıç süresini ve kalıcı bellek tüketimini artırır.

Ultra düşük gecikme hedeflerinde bu değiş tokuş çoğu zaman anlamlıdır; bellek yoğunluğu öncelikli ortamlarda ise esnek heap daha uygun olabilir.

Esnek bellek kullanan ZGC profili

java \
  -XX:+UseZGC \
  -Xms16g \
  -Xmx64g \
  -XX:SoftMaxHeapSize=48g \
  -XX:ZUncommitDelay=300 \
  -Xlog:gc*,safepoint:file=/var/log/app/gc.log:time,uptime,level,tags:filecount=10,filesize=100M \
  -jar application.jar

SoftMaxHeapSize, ZGC’nin normal koşullarda altında kalmaya çalışacağı yumuşak sınırdır. Collector, allocation stall riskini azaltmak için gerekirse -Xmx değerine kadar büyüyebilir.

Örnekte ZGC 48 GB altında kalmaya çalışır; ancak trafik sıçramasında 64 GB’a kadar çıkabilir. Bu, bellek maliyetiyle burst dayanıklılığı arasında kullanışlı bir denge sağlar.

JDK 21 ve 22’de Generational ZGC

java \
  -XX:+UseZGC \
  -XX:+ZGenerational \
  -Xms64g \
  -Xmx64g \
  -XX:+AlwaysPreTouch \
  -jar application.jar

Bu örnekteki -XX:+ZGenerational yalnızca ilgili eski sürüm davranışı için kullanılmalıdır. JDK 24 ve sonrasında bu seçenek kaldırılmıştır.

ZGC’de thread sayısını değiştirmek

ZGC, GC thread sayısını adaptif olarak ayarlayabilir. Bu nedenle -XX:ConcGCThreads başlangıç profilinde sabitlenmemelidir.

GC allocation hızına yetişemiyor, allocation stall oluşuyor ve sistemde boş CPU bulunuyorsa kontrollü bir deney yapılabilir:

-XX:ConcGCThreads=12

Değer çok düşükse collector geride kalabilir. Çok yüksekse GC, uygulama thread’lerinin CPU’sunu tüketerek request latency ve throughput’u bozabilir.

Explicit large pages

Büyük ve sabit heap’lerde, işletim sistemi önceden doğru biçimde hazırlanmışsa:

-XX:+UseLargePages

Large pages, TLB baskısını azaltarak throughput ve latency’ye yardımcı olabilir. Ancak host üzerinde yeterli sayıda sayfa ayrılmadan yalnızca JVM bayrağını eklemek yeterli değildir.

Transparent Huge Pages ile explicit large pages aynı şey değildir. Oracle, latency-sensitive ZGC sistemlerinde Transparent Huge Pages kullanımının istenmeyen latency sıçramaları oluşturabileceğini belirtmektedir. Bu nedenle kernel ayarı, JVM konfigürasyonu ve gerçek workload birlikte test edilmelidir. Oracle ZGC large pages açıklaması

GC loglarında hangi sinyaller izlenmeli?

Collector seçiminden bağımsız olarak aşağıdaki metrikler birlikte değerlendirilmelidir:

  • GC pause p50, p95, p99 ve maksimum değerleri
  • Young, mixed, old ve full collection sayısı
  • GC cycle süresi ve cycle sıklığı
  • Allocation rate
  • Promotion rate
  • Collection sonrası heap occupancy
  • Canlı veri kümesi büyüklüğü
  • Allocation stall sayısı ve süresi
  • Application throughput
  • CPU kullanımı ve CPU throttling
  • Container memory limiti ile toplam RSS farkı
  • Safepoint süresi ve safepoint’e ulaşma gecikmesi
  • Reference processing süresi
  • G1 humongous region ve evacuation failure sayısı

Yalnızca “GC toplam süresi” metriğini izlemek yanıltıcıdır. ZGC’de kısa duraklamalara rağmen concurrent GC CPU maliyeti artabilir. G1’de toplam GC süresi makul görünürken tek bir Full GC tail-latency SLO’sunu ihlal edebilir.

Üretime geçiş için önerilen test yöntemi

GC değişimi kontrollü bir performans mühendisliği çalışması olarak ele alınmalıdır:

  1. Mevcut collector ile temel ölçüm alın.
  2. Aynı JDK dağıtımı, heap ve CPU limitleriyle ikinci collector’ı test edin.
  3. Gerçekçi canlı veri kümesini test başlamadan önce oluşturun.
  4. JVM ısınması ve JIT derlemesi tamamlandıktan sonra ölçüm alın.
  5. Ortalama yükün yanı sıra trafik sıçraması ve uzun süreli soak test uygulayın.
  6. p99.9 latency, throughput, CPU ve RSS değerlerini birlikte karşılaştırın.
  7. Heap dump, JFR ve GC loglarını allocation kaynakları açısından inceleyin.
  8. Canary deployment ile sınırlı üretim trafiğinde doğrulayın.
  9. Otomatik rollback eşiğini GC pause yerine uçtan uca servis SLO’suna bağlayın.

Konteyner ortamında yalnızca -Xmx değerini pod bellek limitine eşitlemeyin. Metaspace, code cache, thread stack’leri, direct buffer’lar, JNI kütüphaneleri ve JVM’nin native yapıları da RSS tüketir. Heap dışında yeterli güvenlik payı bırakılmalıdır.

Hangi senaryoda hangi GC seçilmeli?

G1 GC tercih edin:

  • Gecikme hedefi yüzlerce milisaniye ölçeğindeyse,
  • Throughput, ultra düşük pause süresinden daha önemliyse,
  • CPU limiti sıkıysa,
  • Heap orta veya büyük olsa da canlı veri kümesi yönetilebiliyorsa,
  • Operasyon ekibi yerleşik ve yaygın bir collector istiyorsa,
  • Gerçek workload altında G1 Full GC veya evacuation problemi üretmiyorsa.

ZGC tercih edin:

  • p99.9 ve üzeri tail-latency kritikse,
  • Tek haneli milisaniyelik servis hedefleri bulunuyorsa,
  • Heap onlarca veya yüzlerce gigabaytsa,
  • Canlı veri kümesi büyükse,
  • Uzun G1 evacuation ya da Full GC duraklamaları görülüyorsa,
  • Concurrent GC için CPU kapasitesi ayrılabiliyorsa,
  • Heap’e yeterli allocation headroom verilebiliyorsa.

Sonuç

G1 GC ve ZGC arasında evrensel bir kazanan yoktur. G1 GC, yüksek throughput ile nispeten kısa ve tahmin edilebilir duraklamalar arasında güçlü bir denge sağlar. Bu nedenle çoğu kurumsal Java servisi için hâlâ iyi bir başlangıç noktasıdır.

ZGC’nin ayrıştığı alan, büyük heap ve katı tail-latency hedefleridir. Colored pointers, barrier mekanizmaları, concurrent marking ve concurrent relocation sayesinde pahalı GC işlemlerinin büyük bölümünü uygulamayı durdurmadan gerçekleştirir. Böylece heap büyüklüğünün Stop-The-World süresine etkisini önemli ölçüde azaltır.

Bunun karşılığında ZGC’ye yeterli CPU ve heap headroom sağlanmalıdır. Collector seçimi yalnızca maksimum GC pause karşılaştırmasına indirgenmemeli; throughput, p99.9 yanıt süresi, allocation rate, CPU tüketimi, RSS ve altyapı maliyeti birlikte değerlendirilmelidir.

Kurumsal üretim ortamı için en güvenilir yaklaşım şudur: Önce sade bir JVM konfigürasyonu kurun, ölçüm alın, üretime benzeyen yük altında G1 ve ZGC’yi karşılaştırın ve yalnızca gözlemlenmiş bir problemi çözen bayrağı ekleyin.

Bir sonraki ne okunmalı?

GraalVM Native Image ile Serverless ve Mikroservislerde Cold Start Sorununu Çözmek

Java; güçlü ekosistemi, olgun araçları ve yüksek çalışma zamanı performansıyla mikroservis geliştirmede en güvenilir seçeneklerden biri. Ancak konu serverless fonksiyonlar, kısa ömürlü container’lar ve…

11 dk okuma süresi
Sonraki yazıyı oku

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir