---
title: 'Anında Derleme'
source: 'https://academia.sh/tr/kurslar/jvm-ve-eszamanlilik/aninda-derleme'
course: 'JVM, Eşzamanlılık ve Başarım'
language: tr
updated: '2026-08-23T07:00:56+00:00'
license: 'CC BY-SA 4.0'
---

# Anında Derleme

Sınıf dosyasında hiçbir optimizasyon kararı yoktur: aynı yöntem üç yüz bin kez çağrılsa da sınıf dosyasından okunan komut listesi birebir aynı kalır. Kararı veren taraf programın kendisi değildir; kararın dayandığı alıcı tip sayısını ise koşarken çağıran iş parçacığı üretir. Bir çağrı yeri tek alıcılıdan çok alıcılıya geçtiğinde varsayım geçersizleşir — bu ders hız ölçmez, yalnız komut sayısını ve alıcı tip sayısını sayar.

Önceki derste sınıf dosyasında bulunmayan bir karar görüldü: hangi nesnenin ne zaman
toplanacağı. Bu ders sınıf dosyasında bulunmayan başka bir kararı ölçer — bu kez temizlikle
değil, hızla ilgili bir karar. Bir yöntemin gövdesi sınıf dosyasında hiç değişmeden dururken,
aynı yöntem birinci çağrısında ile bininci çağrısında aynı biçimde koşmayabilir. Bilgisayarlar
Nasıl Çalışır kursu **anında derlemeyi** kavram olarak kurdu ve bu ders onu tekrarlamaz; bu
ders yalnız ölçülen davranışa bakar — karar sınıf dosyasında mı duruyor, yoksa başka bir
yerde mi?

## Sınıf Dosyasında Optimizasyon Kararı Yoktur

Sınıf dosyası derlenip diske yazıldığı andan itibaren sabittir; hiçbir çalışma zamanı olayı
onun içeriğini değiştirmez. Bir yöntem ister hiç çağrılmasın ister milyonlarca kez
çağrılsın, o yöntemin sınıf dosyasındaki komut listesi aynı kalır — çünkü "hangi yöntem sık
çağrılıyor, ne zaman hızlandırılsın" kararı sınıf dosyasının kendisine ait bir bilgi değildir.
Bu, iddia olarak bırakılmaz; aşağıda ölçülür.

Bu, önceki üç derste kurulan örüntünün dördüncü tekrarıdır. Yükleyici hangi sınıfı
tanımladığını sınıf dosyasına yazmaz; sınıf başlatmasını hangi iş parçacığının koşturduğu
sınıf dosyasında görünmez; erişilemez bir nesneyi kimin topladığı da sınıf dosyasının
konusu değildir. Dördüncüsü de aynı biçimdedir: sınıf dosyası bir yöntemin **ne** yaptığını
tam olarak söyler, ama o yöntemin çalışma zamanında **nasıl** koşturulacağı — yorumlanarak
mı, hızlandırılmış bir biçimde mi — hiçbir zaman sınıf dosyasının bir parçası olmaz. Dört
ölçüm de aynı sonuca varıyor: sınıf dosyası kaynağın bir çevirisidir, ama çalışma zamanının
kendisi değildir.

Ölçüm, Java Temelleri kursunun kurduğu `Olcek` aracını kullanır — burada bir ölçü adı olarak
değil, yalnızca sınıf dosyasından bir yöntemin komut listesini okuyan bir araç olarak.

- **JV16** — `Olcek` yalnız bir araçtır; sınıf dosyasından bir yöntemin komut listesini
  okur, hiçbir "eklenen adım" sınıflandırması yapmaz.
- **JV17** — Aynı yöntem, gerçekten yüklenip binlerce kez çağrıldıktan sonra, komut listesi
  aynı sınıf dosyasından yeniden okunur; hiçbir yeniden derleme yapılmaz.
- **JV18** — Çağrı sayısı sayılır (kaç kez çağrıldığı); süre hiç ölçülmez, hiçbir zaman
  damgası okunmaz.

```java
// Olcek.java — sinif dosyasindan bir yontemin komut listesini okuyan arac (Java Temelleri'nden, yalnizca arac olarak)
import java.io.PrintWriter;
import java.io.Writer;
import java.lang.classfile.*;
import java.nio.file.*;
import java.util.*;
import java.util.spi.ToolProvider;

class Olcek {
    static Path derle(String kaynak, String sinif) throws Exception {
        Path d = Files.createTempDirectory("olcek");
        Path k = d.resolve(sinif + ".java");
        Files.writeString(k, kaynak);
        PrintWriter bos = new PrintWriter(Writer.nullWriter());
        ToolProvider.findFirst("javac").orElseThrow().run(bos, bos, "-d", d.toString(), k.toString());
        return d;
    }

    static List<String> komutlar(Path dizin, String sinif, String yontem) throws Exception {
        ClassModel cm = ClassFile.of().parse(dizin.resolve(sinif + ".class"));
        List<String> a = new ArrayList<>();
        for (MethodModel m : cm.methods())
            if (m.methodName().equalsString(yontem) && m.code().isPresent())
                for (CodeElement e : m.code().get())
                    if (e instanceof Instruction i) a.add(i.opcode().name().toLowerCase(Locale.ROOT));
        return a;
    }
}
```

```java
// SicakYol.java — bir yontem cok kez cagrilsa da sinif dosyasindaki komut listesi degismiyor
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Path;
import java.util.List;

public class SicakYol {
    public static void main(String[] args) throws Exception {
        String kaynak = """
            public class Hesaplama {
                public static int uygula(int x) { return x * 2 + 1; }
            }
            """;
        Path dizin = Olcek.derle(kaynak, "Hesaplama");
        List<String> once = Olcek.komutlar(dizin, "Hesaplama", "uygula");

        URLClassLoader yukleyici = new URLClassLoader(new URL[] { dizin.toUri().toURL() });
        Class<?> sinif = Class.forName("Hesaplama", true, yukleyici);
        Method yontem = sinif.getMethod("uygula", int.class);

        int cagriSayisi = 300_000;
        int toplam = 0;
        for (int i = 0; i < cagriSayisi; i++) toplam += (int) yontem.invoke(null, i);

        List<String> sonra = Olcek.komutlar(dizin, "Hesaplama", "uygula");

        System.out.println("cagri sayisi                 : " + cagriSayisi);
        System.out.println("komut listesi cagridan once  : " + once);
        System.out.println("komut listesi cagridan sonra : " + sonra);
        System.out.println("iki liste birebir ayni mi    : " + once.equals(sonra));
        System.out.println("toplam sifir degil mi (kosti): " + (toplam != 0));
    }
}
```

```
cagri sayisi                 : 300000
komut listesi cagridan once  : [iload_0, iconst_2, imul, iconst_1, iadd, ireturn]
komut listesi cagridan sonra : [iload_0, iconst_2, imul, iconst_1, iadd, ireturn]
iki liste birebir ayni mi    : true
toplam sifir degil mi (kosti): true
```

`uygula` yöntemi üç yüz bin kez, gerçekten yüklenmiş bir sınıf üzerinden çağrıldı — `toplam`
sıfır çıkmadı, yani çağrılar gerçekten koştu. Buna karşılık aynı sınıf dosyası çağrılardan
önce ve sonra ayrı ayrı okundu ve iki komut listesi **birebir aynı** çıktı. Bu, "değişmedi
çünkü ölçülmedi" değildir — aynı dosya, aynı yöntem, iki ayrı okuma, aynı sonuç. Yöntemin
sık çağrıldığını fark etmek, onu hızlandırmaya karar vermek, o kararı gerçekten uygulamak —
bunların hiçbiri sınıf dosyasına yazılmaz. Karar, programın kendi kaynak metninde de,
derlediği sınıf dosyasında da hiç yoktur; çalışma zamanında, programın kendisinin
oluşturmadığı bir yerde doğar.

Bilgisayarlar Nasıl Çalışır kursunun kurduğu gibi, sanal makine bayt kodunu doğrudan
yorumlayabilir ya da bir bölümünü çalıştığı işlemciye özgü komutlara çevirebilir; hangi
yolun seçildiği, o bölümün ne sıklıkla koşturulduğuna bağlıdır. Ama bu çeviri, üretildiği
yerde — çalışan programın kendi belleğinde — kalır; kaynağa da, sınıf dosyasına da hiç geri
yazılmaz. Aşağıdaki ölçüm bu ayrımı doğrudan sınar: aynı sınıf dosyası, hızlandırılmaya
en uygun duruma getirildikten sonra bile, kendi diskteki hâliyle karşılaştırılır.

Ölçümün `Method.invoke` ile, dolaylı bir çağrı üzerinden yapılması bilinçli bir seçimdir:
`Hesaplama` sınıfı bu dersin diğer sınıflarıyla aynı dizine derlenmedi, ayrı bir yükleyiciyle
yüklendi ve adı yalnız bir metin olarak taşındı. Bunun nedeni, aynı sınıf dosyasını hem
gerçekten çalıştırıp hem de çağrılardan sonra yeniden okuyabilmektir; sınıf normal bir
komşu dosya olarak yazılsaydı, çalışan kopyanın bayt kodunu ayrıca okumak için yine aynı
yoldan geçmek gerekirdi. Okunan şey, yalnız aynı kaynaktan tekrar derlenmiş bir kopya değil,
**gerçekten üç yüz bin kez çağrılmış olan sınıfın kendisidir.**

## Çağrı Yerinden Geçen Alıcı Tipi: Tek Alıcılıdan Çok Alıcılıya

Bir kararın dayanacağı bir veri olması gerekir, ve o veri de bir yerden gelir. Bir arayüz
üzerinden yapılan her çağrı — bir **çağrı yeri (call site)** — zamanla hangi somut tiplerle
karşılaştığının izini biriktirebilir. Bu ders o izi kendi sayacıyla tutar: bir çağrı
yerinden geçen alıcıların kaç **ayrı** tipten geldiğini `getClass()` kimlikleriyle sayar. Bir
çağrı yeri yalnız tek bir tiple karşılaştıysa **tek alıcılı**, birden çok tiple karşılaştıysa
**çok alıcılı** sayılır.

Bu ayrımın önemi şuradan gelir: `Islenebilir i` referansının sınıf dosyasındaki karşılığı
`i.deger()` çağrısıdır ve bu çağrı hangi somut sınıfı taşıdığını **derleme zamanında**
söylemez — `TipA` da olabilir, `TipB` de. Bir çağrı yeri gerçekte hep aynı tek tiple
karşılaşıyorsa, o bilgi bir hızlandırma fırsatıdır: her çağrıda "hangi sınıf bu" sorusunu
yeniden sormak yerine, geçmişte hep aynı yanıtın geldiği varsayılabilir. Ama bu varsayım
sınıf dosyasının bir parçası değildir — çünkü sınıf dosyası derlendiği anda, çağrı yerinin
çalışma zamanında kaç ayrı tiple karşılaşacağı henüz bilinmez. `Islenebilir` arayüzünün kaç
gerçekleştirimi olduğu da bu bilgiyi vermez: bir arayüzün on gerçekleştirimi olabilir, ama
tek bir çağrı yeri koşum boyunca bunlardan yalnız birini görebilir; alıcı sayısı arayüzün
kendi özelliği değil, o çağrı yerinin geçmişte gördüklerinin bir özetidir.

- **JV19** — Çağrı yerinden geçen alıcı tip sayısı kendi kurduğumuz bir kümeyle
  (`getClass()` kimlikleriyle) sayılır; bu, çalışma zamanının kendi profil düzeneğinin
  değil, o düzeneği modelleyen kendi sayacımızın sonucudur.

```java
// Islenebilir.java — cagri yerinden gecen alicilarin ortak arayuzu
interface Islenebilir { int deger(); }
```

```java
// TipA.java — cagri yerindeki ilk alici tip
class TipA implements Islenebilir { public int deger() { return 1; } }
```

```java
// TipB.java — cagri yerine sonradan katilan ikinci alici tip
class TipB implements Islenebilir { public int deger() { return 2; } }
```

```java
// CagriYeri.java — arayuz uzerinden tek bir cagri yeri
class CagriYeri { static int cagir(Islenebilir i) { return i.deger(); } }
```

```java
// AliciTipi.java — cagri yerinden gecen alici tip sayisi kendi sayacimizla sayilir
import java.nio.file.Path;
import java.util.*;

public class AliciTipi {
    public static void main(String[] args) throws Exception {
        Set<Class<?>> gorulenTipler = new HashSet<>();

        TipA a = new TipA();
        int cagriSayisi = 100_000;
        for (int i = 0; i < cagriSayisi; i++) {
            gorulenTipler.add(a.getClass());
            CagriYeri.cagir(a);
        }
        System.out.println("cagri sayisi (yalniz TipA)     : " + cagriSayisi);
        System.out.println("tek alicili cagri yeri          : " + (gorulenTipler.size() == 1));

        TipB b = new TipB();
        gorulenTipler.add(b.getClass());
        CagriYeri.cagir(b);
        System.out.println("ikinci gerceklestirim yuklendi  : " + (gorulenTipler.size() == 2));
        System.out.println("cok alicili cagri yeri (varsayim gecersiz): " + (gorulenTipler.size() > 1));

        String kaynak = """
            interface Islenebilir { int deger(); }
            class CagriYeri { static int cagir(Islenebilir i) { return i.deger(); } }
            """;
        Path dizin = Olcek.derle(kaynak, "CagriYeri");
        System.out.println("cagir yonteminin komut listesi  : " + Olcek.komutlar(dizin, "CagriYeri", "cagir"));
    }
}
```

```
cagri sayisi (yalniz TipA)     : 100000
tek alicili cagri yeri          : true
ikinci gerceklestirim yuklendi  : true
cok alicili cagri yeri (varsayim gecersiz): true
cagir yonteminin komut listesi  : [aload_0, invokeinterface, ireturn]
```

Yüz bin çağrı boyunca `CagriYeri.cagir` yalnız `TipA` örnekleriyle karşılaştı; sayaç tek
kaldı. `TipB`'nin ilk örneği çağrı yerinden geçtiği an sayaç ikiye çıktı ve çağrı yeri çok
alıcılı oldu. Gerçek çalışma zamanı, tek alıcılı bir çağrı yerini bu bilgiye dayanarak
hızlandırabilir — alıcının hep aynı tip olacağını **varsayarak**; ikinci tip ortaya
çıktığında bu varsayım geçersizleşir ve çalışma zamanı **optimizasyondan döner**. Bu ders bu
dönüşü gerçek bir olay olarak yakalamaz (bunu yapmak gerçekleştirime özgü bir araç
gerektirirdi); yalnız varsayımı geçersiz kılan koşulu — ikinci tipin çağrı yerine katılışını
— kendi sayacıyla **modeller**. Alttaki `cagir` yönteminin komut listesi ise iki durumda da
aynıdır: `invokeinterface` komutu hangi somut tipin geleceğini söylemez, yalnız "bir
`Islenebilir` çağır" der; alıcı sayısının bir mi iki mi olduğu karar sınıf dosyasında hiçbir
iz bırakmaz.

Kararın dayandığı bu veriyi kim üretti? Kod, `gorulenTipler` kümesine her çağrıdan önce
kendi ekleme yaptı — bu ekleme **çağıran iş parçacığının kendi kodunun bir parçasıdır**,
ayrı bir taraf tarafından değil. Gerçek çalışma zamanının profil verisi de aynı ilkeyle
üretilir: bir çağrı yerinden geçen her çağrı, kararı verecek tarafa (çalışma zamanının
kendisine) veri bırakır, ve bu veriyi biriktiren adımın kendisi **çağıran iş parçacığının
yürüttüğü kodun bir parçasıdır**. Kararı veren taraf çağıran değildir, ama kararın
dayanacağı veriyi üreten çağırandır.

Bu ayrım önceki dersteki "kim koşturuyor" ile "kim görüyor" ayrımına benzer, ama aynısı
değildir. Orada bir değeri yazan ile onu gören iki ayrı iş parçacığıydı; burada veriyi
üreten (çağıran) ile o veriye dayanarak karar veren (çalışma zamanı) iki ayrı **taraftır**,
ikisi de aynı iş parçacığı kavramıyla anılamaz — çünkü kararı veren taraf bu derste hiçbir
zaman bir iş parçacığı kimliğiyle gözlemlenmez, yalnız sınıf dosyasında bulunmayışıyla
tanınır.

## Sınırlayıcı Ölçüm: Sıcak Yol Bir Süre İddiası Değildir

Bu derste hiçbir yerde süre ölçülmedi ve ölçülemez: üç yüz bin çağrının ne kadar sürdüğü,
tek alıcılı ile çok alıcılı bir çağrı yerinin hangisinin daha hızlı koştuğu — bunların
hiçbiri bu derste yazılmıyor, çünkü ikisi de çalıştığı makineye, o anki yüke ve
gerçekleştirime bağlıdır. Sayılan tek şey **komut sayısı** ve **alıcı tip sayısı**dır; ikisi
de programın kendi ürettiği, koşumdan koşuma sabit kalan verilerdir.

**Sıcak yol** terimi bu yüzden bir hız iddiası değil, **çağrı yoğunluğuyla** tanımlı bir
bölgedir: bir yöntem çok çağrıldığı için sıcaktır, hızlı koştuğu için değil. Yukarıdaki
ölçüm `uygula` yöntemini üç yüz bin kez çağırarak onu yoğun bir çağrı bölgesine soktu, ama
bu derste ne çağrı öncesi ne çağrı sonrası hiçbir süre karşılaştırması yapılmadı — yalnız
çağrı sayısı ve sınıf dosyasının değişmediği sayıldı. Sıcaklığın bir hız kazancına
dönüşüp dönüşmediği bu dersin sorusu değildir.

Bu sınır iki yönde de işler. Bir yandan, üç yüz bin çağrının "yavaş" ya da "hızlı" olduğuna
dair hiçbir iddia yazılmadı — tek iddia, sınıf dosyasının değişmediğiydi. Öte yandan, tek
alıcılı bir çağrı yerinin çok alıcılı olandan "daha hızlı" koştuğuna dair de hiçbir iddia
yazılmadı — tek ölçülen, alıcı sayısının bir mi iki mi olduğuydu. İki ölçüm de "ne
kadar hızlı" sorusuna hiç yaklaşmadan, yalnız "kaç" ve "aynı mı" sorularına yanıt verdi; bu
kursun ölçüm disiplini burada da aynen geçerlidir.

## Özet

- Sınıf dosyası derlendiği andan sonra sabittir: bir yöntem üç yüz bin kez çağrılsa da
  komut listesi çağrı öncesi ve sonrası birebir aynı kalır.
- Bir yöntemin ne zaman ve nasıl hızlandırılacağı kararı sınıf dosyasında hiç yoktur; karar
  çalışma zamanında, programın kendisinin oluşturmadığı bir yerde doğar.
- Bir çağrı yeri, geçen alıcıların kaç ayrı tipten geldiğine göre tek alıcılı ya da çok
  alıcılı sayılır; ikinci bir gerçekleştirim çağrı yerine katıldığında tek alıcılı varsayım
  geçersizleşir — bu ders bu geçersizleşmeyi (optimizasyondan dönüşü) kendi sayacıyla
  modeller, gerçek bir olay olarak yakalamaz.
- Kararın dayanacağı veriyi (alıcı tip sayısını) üreten taraf çağıran iş parçacığıdır; kararı
  veren taraf ise çağıran değildir.
- Bir çağrı yerinin alıcı sayısı bir mi iki mi olursa olsun, o çağrıyı taşıyan yöntemin komut
  listesi sınıf dosyasında değişmez.
- Sıcak yol çağrı yoğunluğuyla tanımlıdır, süreyle değil; bu ders hiçbir süre ölçmez, yalnız
  komut sayısını ve alıcı tip sayısını sayar.

## Sonraki Adım

Bu konu boyunca sınıf dosyasında bulunmayan üç karar görüldü: sınıfı hangi yükleyicinin
tanımladığı ve hangi iş parçacığının başlattığı, erişilemez bir nesneyi kimin topladığı, ve
bir yöntemi ne zaman hızlandıracağı. Üçünde de kararı veren taraf programın kendisi
değildi — ne kaynak metninde, ne de ondan derlenen sınıf dosyasında hiçbir yerde
bulunamadı. Ama kodu **koşturan** iş parçacıklarını oluşturan yine programın kendisidir:
`new Thread(...)` satırını programın kendisi yazar, `start()` çağrısını programın kendisi
yapar. Sıradaki konu oradan başlar — bir iş parçacığı programın içinden nasıl var ediliyor,
ve gövdesini hangi taraf koşturuyor?
