JVM Konteynerde — Bellek Sınırı Heap'ten Büyüktür
Önce şunu oku: Garbage Collection — Üçlü Takas
30 saniyede özet
Konteynerin bellek sınırı bütün süreci sayar: heap, metaspace, thread yığınları ve native bellek. Heap'e sınırın tamamını verirsen süreç sessizce öldürülür; hiç ayar vermezsen heap dörtte birde kalır.
Asansörde “en fazla 600 kg” yazar. Yolcuları sayarsın, sığıyor; sonra herkes valizini de içeri çeker.
JVM’i bir konteynere koyduğunda bellek sınırı tam bu tartı gibi çalışır. Heap yolculardır; ama tartı valizleri de sayar.
Adım adım oku
- Asansörün sınırı 600 kg.
- Yolcular biniyor; tartı 240 kg gösteriyor.
- Valizler de biniyor; tartı 580 kg'a çıkıyor.
- Tartı ayırmaz: yolcu da valiz de aynı sınırdan yer.
-
Bayt: Ödeme servisi her gece yeniden başlıyor. Loglarda tek bir hata yok!
-
Sen: Heap'e konteynerin tamamını, 2 GB verdik. Yetmiyor mu?
-
Bayt: Sorun belki tam da bu. Heap 2 GB ise geri kalan her şey nereye sığıyor?
-
Bayt: Önce JVM'in belleğini kalem kalem açalım.
Heap, sürecin yalnızca bir parçası
| Kalem | Ne tutar |
|---|---|
| Heap | Senin nesnelerin |
| metaspaceJVM'in yüklenen sınıfların bilgisini heap dışında tuttuğu bellek alanı. Varsayılan olarak tavanı yoktur; çok sınıf yükleyen uygulamada büyür.Java 8'de PermGen'in yerini aldı. MaxMetaspaceSize ile sınırlanabilir; sınıf yükleyici sızıntıları burada görünür.Sözlükte gör → | Yüklenen sınıfların bilgisi |
| Thread yığınları | Her platform thread’i için ayrı bir yığın, varsayılan 1 MiB |
| Code cache | JIT’in ürettiği makine kodu |
| GC yapıları ve direct buffer’lar | Toplayıcının defterleri, ağ ve dosya tamponları |
Konteynerin sınırını Linux’ta cgroupLinux çekirdeğinin bir süreç grubuna bellek ve CPU sınırı koyduğu mekanizma. Docker ve Kubernetes konteyner sınırlarını bununla uygular.JDK 10'dan beri JVM sınırları cgroup'tan okur (UseContainerSupport). Bellek sınırı sürecin bütün belleğini sayar.Sözlükte gör → koyar ve bu kalemlerin hepsini birlikte sayar. Spring Boot’lu tipik bir serviste heap dışı kalemler yüzlerce MiB tutabilir.
Kafam karıştı, daha basit anlat
Heap senin nesnelerin. Ama JVM’in kendi eşyaları da var ve onlar da aynı odada duruyor.
Bir Java servisinin konteyner bellek sınırına neler sayılır?
Her şeyin JVM belleğinde nerede durduğunu seç.
JVM heap’i nasıl seçer?
JDK 10’dan beri JVM konteynerde çalıştığını anlar ve sınırı cgroup’tan okur. Heap’in tavanını bu sınıra göre seçer.
Sınırı 2 GiB olan bir konteynerde Java 21 servisini hiçbir bellek ayarı vermeden başlattın. Heap'in tavanı ne olur? Cevabı göster
Yaklaşık 512 MiB, sınırın dörtte biri. MaxRAMPercentage ayarının varsayılan değeri 25’tir. Konteynerdeki belleğin dörtte üçü heap’e hiç verilmez.
Heap’i sabit bir -Xmx ile değil, sınırın bir yüzdesiyle vermek daha güvenlidir. Sınır büyüyüp küçüldüğünde ayar da onunla gelir.
java -XX:MaxRAMPercentage=50 -jar payment.jarKafam karıştı, daha basit anlat
Hiçbir şey söylemezsen JVM çekingen davranır ve odanın dörtte birini alır. Yüzde ile söylersen ne kadar alacağını bilir.
Sınırı 2 GiB olan bir konteynerde Java 21 servisi hiç bellek ayarı olmadan başlıyor. Heap'in tavanı yaklaşık ne olur?
Heap'i neden sabit bir -Xmx yerine -XX:MaxRAMPercentage ile vermek daha iyi?
İki farklı ölüm
Heap yetmezse JVM OutOfMemoryError fırlatır. Logda yığın izi olur, ayarlıysan bir heap dump da alınır.
Bütün süreç sınırı geçerse karar Java’nın değil, çekirdeğin OOM killerBellek sınırı aşılınca Linux çekirdeğinin bir süreci SIGKILL ile anında sonlandırması. Süreç hiçbir şey yazamaz; çıkış kodu 137 olur.Kubernetes bunu konteyner durumunda OOMKilled olarak gösterir. Java'nın OutOfMemoryError'ından farklıdır: o JVM'in içinde fırlatılır ve iz bırakır.Sözlükte gör →’ının olur. Süreç SIGKILL ile anında biter: çıkış kodu 137, Kubernetes’te durum OOMKilled. Java tek satır bile yazamaz.
Heap dışı kalemleri görmek için Native Memory Tracking açılır: -XX:NativeMemoryTracking=summary ve sonra jcmd PID VM.native_memory summary.
Bir pod gece yeniden başladı. Çıkış kodu 137 ve uygulama logunda hiçbir hata yok. En olası açıklama?
Kendin gör
2 GiB’lık konteynerde bir ödeme servisi var ve trafik tepe noktasına çıkıyor. Heap ayarını ve thread sayısını seç, belleğin nereye gittiğini izle.
2 GiB'lık konteyner: bellek nereye gidiyor?
Tohum 1Oynat ya da adımla.
Şu an ne oldu?
Ödeme servisi açıldı
Konteynerin bellek sınırı 2048 MiB. Heap'in tavanı 2048 MiB. Trafik birazdan tepe noktasına çıkacak.
Görevler0/3
Hiçbir Java hatası yazmadan ölen süreci göraçık
İpucu
Heap'e konteynerin bütün belleğini verirsen ne kalır?
Konteynerde boş bellek varken heap'in yetmediğini göraçık
İpucu
Hiç ayar yapmazsan JVM ne kadar heap seçer?
400 thread ile tepe noktasını sağ atlataçık
İpucu
Heap'e yeterince ver, ama thread yığınlarına da yer bırak.
Olay günlüğü (0)
Henüz olay yok. Oynat veya adımla.
- Varsayılanı oynat.
-Xmx2g: heap büyüdükçe toplam sınırı geçiyor ve süreç sessizce ölüyor. - “Hiç ayar yok”u seç. Bu kez heap dar: konteynerde boş yer varken OutOfMemoryError.
- Yüzde 75’i dene, sonra 400 thread’i aç. Thread yığınları da sınırdan yiyor.
- 400 thread’le tepe noktasını sağ atlatan ayarı bul.
Simülatörde MaxRAMPercentage=75 az thread'le ayakta kaldı ama 400 thread'de süreç öldürüldü. Neden?
Tuzaklar
-Xmx’i sınıra eşitlemek. Heap dışı kalemlere yer kalmaz; süreç yükte öldürülür.
Çok küçük CPU sınırı. JVM işlemci sayısını da konteynerden okur. İkiden az işlemci görürse G1 yerine Serial GC seçer ve havuzlarını küçük kurar.
Sınırı ölçmeden seçmek. Heap dışı pay her serviste farklıdır; NMT ile bir kez ölçülür ve yüzde ona göre ayarlanır.
Bu ödeme servisi yoğun saatte iz bırakmadan yeniden başlıyor ve GC duraklamaları uzun. Sorunlu satırları işaretle.
Derinleş · Ödeme servisi: konteynere uygun JVM ayarları 4 dosya · ~36 satır · ilk okumada atlayabilirsin
Kendini sına
Önce hızlı bir ısınma: puan yok, kayıt yok. Sonra asıl sorular.
Konteynerin bellek sınırı yalnızca heap'i sayar.
Heap sabit ama sürecin belleği günler içinde yavaşça artıyor ve sonunda OOMKilled oluyor. Nasıl ilerlersin?
Aklında kalacak üç şey
- 1 Konteyner sınırı heap'in değil bütün sürecin sınırıdır. Metaspace, thread yığınları, code cache ve native bellek de aynı sınırdan yer.
- 2 Ayar yoksa JVM heap'e sınırın dörtte birini verir. Heap, -Xmx yerine MaxRAMPercentage ile sınırın bir yüzdesi olarak verilir ve heap dışına pay kalır.
- 3 Heap yetmezse JVM OutOfMemoryError fırlatır ve iz bırakır. Süreç sınırı aşarsa çekirdek onu öldürür: OOMKilled, çıkış kodu 137, Java'dan tek satır yok.
5 kart sonraki derste seni bekliyor
Bu dersin üstüne kurulanlar
Bunlar bu dersi temel alıyor; hazır olduğunda devam edebilirsin.