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:
- Bir Concurrent Start young collection yapılır.
- Old Generation içindeki erişilebilir nesneler uygulamayla eş zamanlı işaretlenir.
- Remark duraklamasında işaretleme sonuçlandırılır.
- Cleanup aşamasında bölgelerin canlılık oranları hesaplanır.
- 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
OutOfMemoryErrorriskini 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:+ZGenerationalile etkinleştirilir. - JDK 23’te generational mod varsayılan hale gelmiştir.
- JDK 24 itibarıyla non-generational ZGC kaldırılmış ve
ZGenerationalseç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 özellik | G1 GC | ZGC |
|---|---|---|
| Temel hedef | Throughput ve makul pause süresi dengesi | Ultra düşük GC pause ve ölçeklenebilirlik |
| Çalışma modeli | Mostly concurrent | Pahalı GC işlerinde fully concurrent yaklaşım |
| Heap organizasyonu | Region tabanlı, generational | Region tabanlı, JDK 24+ yalnızca generational |
| Marking | Büyük ölçüde concurrent; başlangıç, remark ve cleanup duraklamaları içerir | Büyük ölçüde concurrent, kısa faz geçişleri içerir |
| Nesne taşıma | Collection set evacuation işlemi Stop-The-World | Relocation büyük ölçüde concurrent |
| Referans takibi | Card table, remembered set ve write barrier | Colored pointers ile load/store barrier’lar |
| Pause hedefi | MaxGCPauseMillis ile ergonomik hedef | Heap boyutundan bağımsız, çok kısa pause tasarımı |
| Full GC riski | Allocation veya evacuation failure durumunda uzun Full GC oluşabilir | Klasik uzun Full GC ihtiyacını concurrent relocation ile önlemeyi amaçlar |
| Throughput | Genellikle daha yüksek | Concurrent çalışma ve barrier’lar nedeniyle daha düşük olabilir |
| CPU gereksinimi | Dengeli; pause sırasında yüksek paralellik | GC cycle boyunca uygulamayla paylaşılacak CPU headroom ister |
| Heap headroom ihtiyacı | Orta; reserve alan yine kritiktir | Concurrent cycle sırasında allocation için daha yüksek boşluk gerekebilir |
| Large heap uygunluğu | Onlarca GB ve üzeri heap’lerde kullanılabilir | Yü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 olgunluk | Uzun 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
| Kriter | G1 GC lehine durum | ZGC lehine durum | Ölçülmesi gereken metrik |
| Gecikme SLO’su | 50–300 ms düzeyindeki GC duraklamaları tolere edilebiliyor | Tek haneli milisaniyelik tail-latency kritik | p99, p99.9, maksimum pause |
| Heap büyüklüğü | Orta ve büyük heap | Çok büyük heap veya yüksek live-set | Heap occupancy, live-set |
| Throughput | Maksimum işlem hacmi öncelikli | Bir miktar throughput maliyeti kabul edilebilir | İşlem/saniye, CPU/request |
| Allocation rate | Düşük veya orta | Yüksek fakat yeterli CPU ve heap headroom mevcut | MB/s veya GB/s allocation |
| CPU kapasitesi | CPU limiti sıkı | Concurrent GC için rezerv CPU bulunuyor | CPU saturation, steal time |
| Bellek maliyeti | Sıkı bellek limiti | Latency için daha fazla headroom ayrılabilir | RSS, committed heap, native memory |
| Trafik yapısı | Öngörülebilir ve dengeli | Ani allocation artışları yaşanıyor | Allocation spike, burst süresi |
| Büyük nesneler | Humongous allocation düşük | Büyük nesneler G1’de fragmentation yaratıyor | Humongous region sayısı |
| Konteyner limiti | Küçük ve sıkı pod limitleri | Büyük, CPU garantili instance veya pod | cgroup memory/CPU limitleri |
| Operasyonel deneyim | Ekip G1 loglarını ve davranışını iyi tanıyor | Ekip ZGC benchmark ve kapasite testlerini yapabiliyor | Incident ve tuning tecrübesi |
| JDK sürümü | Eski veya kurumsal olarak standartlaştırılmış sürüm | Güncel LTS veya JDK 24+ kullanılabiliyor | Dağı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:
- Mevcut collector ile temel ölçüm alın.
- Aynı JDK dağıtımı, heap ve CPU limitleriyle ikinci collector’ı test edin.
- Gerçekçi canlı veri kümesini test başlamadan önce oluşturun.
- JVM ısınması ve JIT derlemesi tamamlandıktan sonra ölçüm alın.
- Ortalama yükün yanı sıra trafik sıçraması ve uzun süreli soak test uygulayın.
- p99.9 latency, throughput, CPU ve RSS değerlerini birlikte karşılaştırın.
- Heap dump, JFR ve GC loglarını allocation kaynakları açısından inceleyin.
- Canary deployment ile sınırlı üretim trafiğinde doğrulayın.
- 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.