JVM: Garbage Collection ve JIT
Java'nın "yaz bir kez, her yerde çalış" gücü ve performansının arkasında JVM (Java Virtual Machine) vardır. JVM'in iki kritik mekanizması, programının belleğini ve hızını otomatik olarak yönetir: Garbage Collection (ç…
JVM: Garbage Collection ve JIT
Java'nın "yaz bir kez, her yerde çalış" gücü ve performansının arkasında JVM (Java Virtual Machine) vardır. JVM'in iki kritik mekanizması, programının belleğini ve hızını otomatik olarak yönetir: Garbage Collection (çöp toplama — belleği) ve JIT derleyici (hızı). İkisini de sen elle yönetmezsin; ama nasıl çalıştıklarını anlamak, performanslı ve sızıntısız kod yazmanı sağlar.
Garbage Collection (çöp toplama)
C/C++'ta belleği elle ayırır ve free ile boşaltırsın — unutursan sızıntı, erken boşaltırsan
çökme olur. Java'da bunu GC yapar: artık ulaşılamayan (hiçbir referansı kalmayan) nesneleri
otomatik tespit edip belleği geri kazanır.
List<byte[]> tutucu = new ArrayList<>(); tutucu.add(new byte[100_000]); // bellek kullanılır tutucu.clear(); // referans gitti -> nesneler çöp System.gc(); // GC ÖNERİSİ (garanti değil)
Örnek 1 (./Ornek1.java) Runtime ile bellek kullanımını izler, çöp üretip GC'nin topladığını
gösterir ve WeakReference ile zayıf referansları gösterir (yalnızca weak referans kalan nesne
GC tarafından toplanabilir — önbellekler için kullanılır).
Önemli noktalar:
System.gc()yalnızca öneridir — JVM ne zaman/toplayıp toplamayacağına kendi karar verir. Üretimde elle çağırma.- Referans türleri: güçlü (normal),
WeakReference("varsa kullan"),SoftReference(bellek baskısında atılır),PhantomReference(temizlik kancası). - GC türleri: G1 (Java'nın varsayılanı), ZGC/Shenandoah (çok düşük gecikme; topic 20'de değindik). Her biri gecikme/verim dengesini farklı kurar.
- Bellek sızıntısı (Java'da da olur!): İstemeden tutulan referanslar — büyüyen statik koleksiyonlar, kapatılmayan kaynaklar, dinleyici (listener) birikmesi. GC ulaşılabilir nesneyi toplayamaz.
JIT (Just-In-Time) derleyici
JVM, Java kodunu önce platformdan bağımsız bytecode'a derler (javac). Çalışma anında bu
bytecode yorumlanır (interpret). Ama JVM, sık çalışan ("hot") metotları tespit edip onları
çalışma anında yerel makine koduna derler — işte JIT budur. Sonuç: büyük hızlanma.
Bunun gözlemlenebilir sonucu "ısınma" (warmup): bir metot ilk çağrılarda (yorumlanırken) genelde yavaş, çok çağrıldıktan sonra (JIT derledikten sonra) genelde hızlıdır. JIT ayrıca satır içi alma (inlining), ölü kod eleme, döngü açma gibi agresif optimizasyonlar yapar.
Örnek 2 (./Ornek2.java) aynı işi tur tur ölçer ve önemli bir gerçeği gösterir: sayılar tutarlı
bir "hep azalan" eğri vermeyebilir! İlk tur genelde en yavaştır ama sonraki turlar GC
duraklamaları, OS zamanlaması ve JIT'in farklı anlarda devreye girmesi yüzünden dalgalanır.
Asıl ders — mikro-benchmark tuzağı: Güvenilir performans ölçümü
System.nanoTime()ile elle yapılmaz. Warmup, GC gürültüsü ve JIT optimizasyonları (ölü kod eleme, sabit katlama, inlining) sonucu çarpıtır; bu yüzden naif ölçümler yanıltıcıdır. Doğru ölçüm için JMH (Java Microbenchmark Harness) kullanılır — warmup, çoklu iterasyon ve istatistiksel analizi o doğru yapar.
Bellek alanları (kısa)
JVM belleği başlıca: Heap (nesneler — GC burada çalışır; genç/yaşlı kuşak), Stack (her
thread'in metot çağrıları ve yerel değişkenleri), Metaspace (sınıf meta-verisi). OutOfMemoryError
(heap dolu) ve StackOverflowError (stack dolu — topic 69) bu alanlarla ilgilidir.
Özet
JVM'in iki otomatik mekanizmasını öğrendik: ulaşılamayan nesneleri toplayan Garbage Collection (referans türleri, GC türleri, sızıntı nedenleri; Örnek 1) ve sık çalışan kodu makine koduna derleyip hızlandıran JIT (warmup etkisi, mikro-benchmark tuzağı; Örnek 2). Bunları anlamak, bellek-dostu ve performanslı Java yazmanın temelidir. Bu, ileri başlıklar batch'ini tamamlıyor.
▶ Kod Örnekleri(2)
Ornek1
çalıştırılabilir1// Ornek1: Garbage Collection (çöp toplama) — bellek kullanımı ve WeakReference.
2// Çalıştırma: java Ornek1.java
3import java.lang.ref.WeakReference;
4import java.util.ArrayList;
5import java.util.List;
6
7public class Ornek1 {
8
9 public static void main(String[] args) {
10 Runtime rt = Runtime.getRuntime();
11 System.out.println("Maks bellek : " + mb(rt.maxMemory()) + " MB");
12 System.out.println("Başlangıç kullanılan: " + mb(kullanilan(rt)) + " MB");
13
14 // Çok sayıda nesne üret (çöp olacaklar). GC bunları toplayacak.
15 List<byte[]> tutucu = new ArrayList<>();
16 for (int i = 0; i < 100; i++) tutucu.add(new byte[100_000]); // ~10 MB
17 System.out.println("100 blok sonrası : " + mb(kullanilan(rt)) + " MB");
18
19 tutucu.clear(); // referanslar gitti -> nesneler ARTIK ULAŞILAMAZ (çöp)
20 System.gc(); // GC ÖNERİSİ (garanti değil)
21 uyu(100);
22 System.out.println("clear + gc sonrası : " + mb(kullanilan(rt)) + " MB (GC topladı)");
23
24 // WeakReference: zayıf referans. Nesneye SADECE weak referans kalırsa GC onu toplayabilir.
25 Object nesne = new Object();
26 WeakReference<Object> zayif = new WeakReference<>(nesne);
27 System.out.println("\nweak.get() (güçlü ref varken): " + (zayif.get() != null ? "var" : "null"));
28 nesne = null; // güçlü referansı kaldır -> yalnızca weak kaldı
29 System.gc();
30 uyu(100);
31 System.out.println("weak.get() (güçlü ref yokken): " + (zayif.get() != null ? "var" : "null (GC topladı)"));
32
33 System.out.println("""
34
35 --- Garbage Collection (çöp toplama) ---
36 Java'da belleği SEN yönetmezsin (C/C++ gibi free yok); ULAŞILAMAYAN nesneleri GC otomatik toplar.
37 Bir nesneye hiçbir güçlü referans kalmadığında çöptür ve GC onu temizleyebilir.
38 System.gc() yalnızca ÖNERİDİR (ne zaman/topkanacağı garanti değil); üretimde elle çağırma.
39 WeakReference: 'varsa kullan, GC alırsa sorun değil' (önbellekler). SoftReference: bellek baskısında atılır.
40 GC türleri: G1 (varsayılan), ZGC/Shenandoah (düşük gecikme - topic 20). Bellek sızıntısı = istemeden
41 tutulan referanslar (statik koleksiyonlar, kapanmayan kaynaklar).""");
42 }
43
44 static long kullanilan(Runtime rt) { return rt.totalMemory() - rt.freeMemory(); }
45 static long mb(long b) { return b / (1024 * 1024); }
46 static void uyu(long ms) { try { Thread.sleep(ms); } catch (InterruptedException ignored) {} }
47}Ornek2
çalıştırılabilir1// Ornek2: JIT derleyici — "ısınma" (warmup) ve neden elle mikro-benchmark GÜVENİLMEZ.
2// Çalıştırma: java Ornek2.java
3public class Ornek2 {
4
5 // Sıcak (hot) metot: çok çağrılınca JIT bunu yerel makine koduna derler.
6 static long hesapla(int n) {
7 long toplam = 0;
8 for (int i = 0; i < n; i++) toplam += (i * 31L) ^ (i >> 2);
9 return toplam;
10 }
11
12 public static void main(String[] args) {
13 int N = 2_000_000;
14 long kontrol = 0;
15
16 // Aynı işi arka arkaya birkaç tur ölç. İLK turlar genelde yavaştır (kod yorumlanıyor,
17 // JIT henüz derlememiş); sonraki turlar JIT derlemesi + önbellek ile genelde hızlanır.
18 // ANCAK sayılar tura/makineye göre DEĞİŞİR ve gürültülüdür — asıl ders budur.
19 System.out.println("Tur tur çalıştırma süreleri (µs):");
20 long[] sureler = new long[8];
21 for (int tur = 0; tur < sureler.length; tur++) {
22 long t0 = System.nanoTime();
23 kontrol += hesapla(N);
24 sureler[tur] = (System.nanoTime() - t0) / 1_000;
25 System.out.printf(" tur %d: ~%5d µs%n", tur + 1, sureler[tur]);
26 }
27
28 System.out.println("(kontrol toplamı kullanıldı: " + (kontrol != 0) + ")");
29 System.out.printf("İlk tur ~%d µs, son tur ~%d µs%n", sureler[0], sureler[sureler.length - 1]);
30
31 System.out.println("""
32
33 --- JIT (Just-In-Time) derleyici ---
34 JVM kodu önce BYTECODE olarak YORUMLAR; sık çalışan ("hot") metotları çalışma anında
35 YERLEŞİK MAKİNE KODUNA derler (JIT) -> büyük hızlanma. Bu yüzden 'ısınma' (warmup) vardır:
36 ilk turlar genelde yavaş, sonraki turlar hızlıdır.
37 AMA yukarıdaki sayılara dikkat: tutarlı bir 'hep azalan' eğri GÖREMEYEBİLİRSİN!
38 Çünkü elle ölçüm GÜRÜLTÜLÜDÜR: GC duraklamaları, OS zamanlaması, JIT'in farklı anlarda
39 devreye girmesi, ölü-kod eleme ve inlining sonucu çarpıtır.
40 İŞTE ASIL DERS: güvenilir performans ölçümü elle System.nanoTime ile YAPILMAZ.
41 JMH (Java Microbenchmark Harness) kullan — warmup, çoklu iterasyon ve istatistik onunla doğru yapılır.""");
42 }
43}