JVM Mimarisi
JVM (Java Virtual Machine), Java'nın "bir kez yaz, her yerde çalıştır" (write once, run anywhere) sözünü mümkün kılan motordur. Java kodu doğrudan makine koduna değil, platformdan bağımsız bytecode'a derlenir; JVM bu…
JVM Mimarisi
JVM (Java Virtual Machine), Java'nın "bir kez yaz, her yerde çalıştır" (write once, run
anywhere) sözünü mümkün kılan motordur. Java kodu doğrudan makine koduna değil, platformdan
bağımsız bytecode'a derlenir; JVM bu bytecode'u her işletim sisteminde çalıştırır. JVM'in iç
yapısını anlamak, bellek davranışını, performansı ve hata türlerini (OutOfMemoryError,
StackOverflowError) kavramanı sağlar.
Derleme ve çalıştırma akışı
Merhaba.java --(javac: derleme)--> Merhaba.class (bytecode) --(JVM: yükle+yürüt)--> çalışma
Bytecode platformdan bağımsızdır: aynı .class dosyası Windows, Linux, macOS'ta — o platformun
JVM'i varsa — aynı şekilde çalışır. Taşınabilirliğin sırrı budur.
JVM'in üç ana bileşeni
-
Class Loader (Sınıf Yükleyici):
.classdosyalarını JVM'e yükler — gecikmeli (lazy), yani ihtiyaç duyulunca. Hiyerarşiktir:- Bootstrap: JDK çekirdek sınıfları (
java.lang.*—Stringgibi).getClassLoader()bunlar içinnulldöndürür. - Platform: Diğer standart kütüphaneler.
- Application: Senin classpath'indeki sınıflar.
- Bootstrap: JDK çekirdek sınıfları (
-
Runtime Data Areas (Bellek Alanları): Çalışma sırasında belleği bölümler:
- Heap: Tüm nesnelerin yaşadığı paylaşılan alan; GC burada çalışır (genç/yaşlı
kuşak). Dolarsa
OutOfMemoryError. - Stack: Her thread'e özel; metot çağrıları ve yerel değişkenler. Çok derin özyineleme
StackOverflowError(topic 69) verir. - Metaspace: Sınıf meta-verileri (yapı, metot bilgileri).
- PC Register & Native Method Stack: Yürütme konumu ve yerel (JNI) çağrılar.
- Heap: Tüm nesnelerin yaşadığı paylaşılan alan; GC burada çalışır (genç/yaşlı
kuşak). Dolarsa
-
Execution Engine (Yürütme Motoru):
- Interpreter: Bytecode'u yorumlar (başlangıçta).
- JIT Compiler: Sık çalışan kodu makine koduna derler — hızlandırır (topic 82).
- Garbage Collector: Ulaşılamayan nesneleri temizler (topic 82).
Örnek 1 (./Ornek1.java) JVM'i tanır (vm adı/sürüm), heap bellek kullanımını ve çekirdek sayısını
gösterir. Örnek 2 (./Ornek2.java) bir sınıfın Class nesnesini, sınıf yükleyiciyi (bootstrap →
null) ve bytecode/yükleme kavramını gösterir.
Stack tabanlı sanal makine
JVM, gerçek bir CPU'yu taklit eder ama yığın tabanlı (stack-based) bir komut seti yürütür
(register tabanlı gerçek CPU'ların aksine). Bytecode komutları operandları bir yığına itip çeker
(iload, iadd, invokevirtual...). Bu tasarım, bytecode'u kompakt ve platformdan bağımsız kılar;
JIT ise bunu çalışma anında platforma özel register tabanlı makine koduna çevirir.
Neden bu kadar önemli?
- Performans: Heap/GC ayarları (
-Xmx, GC seçimi) uygulama davranışını belirler. - Hata teşhisi:
OutOfMemoryError(heap),StackOverflowError(stack),ClassNotFoundException(class loader) — hangi bileşenin sorun çıkardığını anlarsın. - Çok dillilik: JVM yalnızca Java değil, Kotlin, Scala, Groovy, Clojure gibi dilleri de çalıştırır — hepsi bytecode'a derlenir.
Özet
JVM'in bytecode'u çalıştıran, platformdan bağımsızlık sağlayan motor olduğunu; üç ana bileşenini — Class Loader, Runtime Data Areas (heap/stack/metaspace), Execution Engine (interpreter
- JIT + GC) — ve çalışma akışını (Örnek 1–2) öğrendik; stack-tabanlı tasarımı ve neden önemli olduğunu gördük. Sırada, sık karıştırılan üç kavramın netleştirilmesi: JDK vs JRE vs JVM.
▶ Kod Örnekleri(2)
Ornek1
çalıştırılabilir1// Ornek1: JVM çalışma zamanı — bellek alanları ve sistem bilgileri.
2// Çalıştırma: java Ornek1.java
3public class Ornek1 {
4
5 public static void main(String[] args) {
6 Runtime rt = Runtime.getRuntime();
7
8 // JVM'i tanıyalım:
9 System.out.println("JVM adı : " + System.getProperty("java.vm.name"));
10 System.out.println("JVM sürümü: " + System.getProperty("java.vm.version"));
11 System.out.println("Java sürümü: " + System.getProperty("java.version"));
12 System.out.println("Mimari : " + System.getProperty("os.arch"));
13
14 // Heap (yığın) bellek alanı — nesnelerin yaşadığı yer (GC burada çalışır):
15 System.out.println("\nHeap belleği:");
16 System.out.println(" maks : " + mb(rt.maxMemory()) + " MB");
17 System.out.println(" ayrılan : " + mb(rt.totalMemory()) + " MB");
18 System.out.println(" kullanılan: " + mb(rt.totalMemory() - rt.freeMemory()) + " MB");
19
20 // İşlemci sayısı (paralellik kararlarında kullanılır):
21 System.out.println("\nKullanılabilir çekirdek: " + rt.availableProcessors());
22
23 System.out.println("""
24
25 --- JVM mimarisi: çalışma zamanı ---
26 JVM, derlenmiş BYTECODE'u (.class) çalıştıran sanal makinedir. Başlıca bellek alanları:
27 HEAP : tüm nesnelerin yaşadığı paylaşılan alan (GC burada çalışır; genç/yaşlı kuşak).
28 STACK : her thread'e özel; metot çağrıları + yerel değişkenler (StackOverflow burada).
29 METASPACE: sınıf meta-verileri (sınıf yapısı, metot bilgisi).
30 PC register & native method stack: yürütme konumu ve yerel (JNI) çağrılar.
31 Bileşenler: Class Loader (yükle), Runtime Data Areas (bellek), Execution Engine (yorumlayıcı + JIT + GC).""");
32 }
33
34 static long mb(long b) { return b / (1024 * 1024); }
35}Ornek2
çalıştırılabilir1// Ornek2: Sınıf yükleme ve bytecode — Class nesnesi, JVM bir sınıfı nasıl temsil eder.
2// Çalıştırma: java Ornek2.java
3public class Ornek2 {
4
5 static class Ornek { int x; void selam() {} void hesapla(int n) {} }
6
7 public static void main(String[] args) {
8 // Her yüklenen sınıf, JVM'de bir Class nesnesiyle temsil edilir (meta-veri).
9 Class<?> sinif = Ornek.class;
10 System.out.println("Sınıf adı : " + sinif.getName());
11 System.out.println("Basit ad : " + sinif.getSimpleName());
12 System.out.println("Metot sayısı : " + sinif.getDeclaredMethods().length);
13 System.out.println("Alan sayısı : " + sinif.getDeclaredFields().length);
14
15 // Sınıfı kim yükledi? (Class Loader hiyerarşisi)
16 System.out.println("\nOrnek2'yi yükleyen ClassLoader: " + Ornek2.class.getClassLoader());
17 // JDK çekirdek sınıflarını (String) bootstrap class loader yükler -> null görünür.
18 System.out.println("String'i yükleyen ClassLoader : " + String.class.getClassLoader()
19 + " (null = bootstrap loader)");
20
21 // Bytecode: Java kaynağı önce .class (bytecode) olur, sonra JVM çalıştırır.
22 System.out.println("\nBu sınıf bytecode olarak yüklendi ve yorumlanıp JIT ile derlenir.");
23
24 System.out.println("""
25
26 --- Sınıf yükleme ve bytecode ---
27 Akış: .java --(javac)--> .class (BYTECODE) --(JVM)--> çalıştırma.
28 Bytecode platformdan BAĞIMSIZDIR; aynı .class her işletim sisteminde çalışır ('write once, run anywhere').
29 Her yüklü sınıf bir Class<?> nesnesiyle temsil edilir (reflection bunu kullanır - topic 16).
30 Class Loader hiyerarşisi (gecikmeli/lazy yükler): Bootstrap (JDK çekirdeği) -> Platform -> Application.
31 JVM gerçek bir CPU'yu taklit eder: yığın tabanlı (stack-based) bir komut seti yürütür.""");
32 }
33}