Bölüm 1 / 14
.NET nedir
CLR, IL, JIT; .NET Framework / Core / 8+ farkları.
11 dakikalık okuma
Üç hattır C# yazıyorsun ama kodun çalışana kadar neler olduğunu henüz açmadık. Bu ders o boşluğu dolduruyor: kaynak kodun ara dile çevrilmesi, çalışma zamanının onu makine koduna derlemesi, çöp toplayıcının bellek yönetimi ve "Framework mü Core mu" sorusunun bugünkü cevabı. Bunlar akademik ayrıntı değil — sözlü sınavın açılış sorusu genelde buradan gelir ve üretimde soğuk başlatma, dağıtım boyutu, sürüm desteği kararlarını doğrudan bu bilgi belirler.
01Ne öğreneceksin
- .NET'in parçaları: dil, derleyici, çalışma zamanı, sınıf kütüphanesi, SDK
- Derleme yolculuğu: kaynak → IL → derleme birimi (assembly) → JIT → makine kodu
- CLR'ın sağladıkları: tip güvenliği, çöp toplama, istisna yönetimi, JIT
- Kademeli derleme (tiered compilation), ReadyToRun ve NativeAOT ayrımı
- .NET Framework · .NET Core · .NET 5+ farkları; .NET Standard'ın bugünkü yeri
- LTS/STS sürüm politikası ve hangi projede hangi sürümün seçileceği
- Yayımlama seçenekleri: çerçeveye bağımlı, kendine yeterli, tek dosya, trimleme
- Soğuk başlatma ve bellek imaları: sunucu/istemci GC, ısınma süresi
02.NET tam olarak nedir
Günlük dilde ".NET" tek bir şeymiş gibi konuşulur; aslında bir platform, yani birlikte çalışan parçalar kümesidir:
- Diller — C#, F#, Visual Basic. Hepsi aynı ara dile derlenir.
- Derleyici — C# için Roslyn. Kaynağı ara dile çevirir; ayrıca kod analizi ve kod üretimi (source generator) hizmetlerini verir. 1. derste açtığın analizörler Roslyn üzerinde çalışır.
- Çalışma zamanı (CLR) — ara dili makine koduna çeviren, belleği yöneten, tip güvenliğini ve istisnaları yürüten katman.
- Temel sınıf kütüphanesi (BCL) —
System.*ad alanları: koleksiyonlar, dosya, ağ, LINQ, JSON. Şu ana kadar kullandığın her şey. - SDK ve araçlar —
dotnetkomut satırı, MSBuild, NuGet, test koşucusu. 23. hattaki komut referansında bu araçların parametrelerini bulacaksın.
Ayrım şurada işe yarar: SDK geliştirmek için, çalışma zamanı (runtime) çalıştırmak için gerekir. Sunucuya SDK kurmazsın; ASP.NET Core çalışma zamanı yeter — ya da uygulamayı kendine yeterli yayımlayıp hiçbir şey kurmazsın.
dotnet --info # kurulu SDK'lar, çalışma zamanları, RID
dotnet --list-sdks
dotnet --list-runtimes
03Derleme yolculuğu: IL ve JIT
C# doğrudan makine koduna derlenmez. İki aşama vardır:
Program.cs ──(Roslyn, derleme zamanı)──▶ IL + metadata ──▶ Uygulama.dll
│
(çalışma zamanı, ilk çağrıda) ▼
JIT ──▶ makine kodu
IL (Intermediate Language / CIL), işlemciden bağımsız bir yığın tabanlı ara
dildir. Derleyicinin ürettiği .dll dosyası bir derleme birimidir
(assembly) ve içinde üç şey bulunur: IL kodu, metadata (tiplerin, üyelerin,
imzaların tanımı) ve manifest (ad, sürüm, bağımlılıklar). Metadata'nın orada
olması, yansımanın (reflection), serileştirmenin ve bağımlılıkları çalışma zamanında
çözmenin nasıl mümkün olduğunu açıklar.
.dll dosyasını ILSpy ya da ildasm ile açtığında C#'ın
derleyici tarafından nasıl açıldığını da görürsün: foreach'in
GetEnumerator/MoveNext çağrılarına, using'in
try/finally'ye, async metodun bir durum makinesi sınıfına
dönüştüğünü. 3. hatta "derleyici bunu şuna çevirir" dediğimiz her cümleyi burada
gözünle doğrulayabilirsin.
JIT (Just-In-Time), IL'i ilk çağrıldığı anda makine koduna çevirir ve sonuç bellekte tutulur; ikinci çağrı doğrudan makine kodudur. Kazanç, kodun çalıştığı makinenin işlemci yeteneklerine göre derlenebilmesidir. Bedeli, ilk çağrıların yavaşlığıdır — ısınma (warm-up) denen olgu budur ve ölçüm yaparken ilk turların atlanmasının sebebidir.
Modern .NET bunu iki mekanizmayla iyileştirir. Kademeli derleme (tiered compilation): metot önce hızlı ama az iyileştirilmiş biçimde derlenir; sıcak olduğu anlaşılırsa (çağrı sayacı) arka planda yeniden, agresif iyileştirmelerle derlenir. Uzun süren döngülerin ortasında bile ikinci kademeye geçebilmek için OSR (on-stack replacement) kullanılır. Dinamik PGO ise çalışma zamanında toplanan profil bilgisini kullanarak sanal çağrıları tahmine dayalı olarak doğrudan çağrıya çevirebilir; .NET 8'den beri varsayılan olarak açıktır.
Üçüncü seçenek, derlemenin bir kısmını önceden yapmaktır: ReadyToRun (R2R) yayımlamada makine kodunu da pakete koyar — soğuk başlatma kısalır, dosya büyür, kod yine de gerektiğinde yeniden derlenebilir. NativeAOT ise IL'i tamamen önceden makine koduna çevirir: JIT yoktur, başlatma milisaniyeler düzeyine iner ve bellek ayak izi küçülür; karşılığında yansıma ve çalışma zamanı kod üretimi kısıtlanır, uygulama tek platform/mimari için üretilir. Kısa ömürlü, çok sık başlayan işlerde (sunucusuz işlevler, komut satırı araçları) değerlidir; klasik bir web API'de çoğu zaman gerekmez.
04CLR: yönetilen çalışma zamanı
C#'a "yönetilen dil" denmesinin sebebi, kodun doğrudan işletim sistemine değil CLR'a (Common Language Runtime) teslim edilmesidir. CLR'ın verdiği hizmetler:
- Bellek yönetimi — nesneleri yığında ayırır, çöp toplayıcı (GC) erişilemez
olanları toplar. Nesillerin nasıl çalıştığını 3. hattın 9. dersinde görmüştün;
buradaki ek bilgi GC kipleridir: workstation GC masaüstü uygulamaları
için gecikmeyi, server GC ise çekirdek başına ayrı yığın ve ayrı toplama
iş parçacığıyla verimi hedefler. ASP.NET Core sunucu uygulamalarında server GC
varsayılandır; kapsayıcıda çalışırken CPU/bellek sınırlarına göre ayarlanması gerekir
(
ServerGarbageCollection,ConcurrentGarbageCollection). - Tip güvenliği ve doğrulama — IL yüklenirken tip uyumu denetlenir; keyfi
bellek erişimi engellenir (
unsafebloğu bilinçli bir istisnadır). - İstisna yönetimi — yığın çözme (stack unwinding) ve
try/catch/finallysemantiği çalışma zamanının işidir. - Derleme birimi yükleme — bağımlılıklar çalışma zamanında bulunur ve
yüklenir. Eski .NET Framework'te bu iş AppDomain ve makine genelindeki GAC ile
yapılırdı; modern .NET'te
AssemblyLoadContextvardır ve bağımlılıklar uygulama klasöründeki.deps.jsondosyasına göre çözülür. - Birlikte çalışma — yerel (native) kütüphanelere
P/Invokeile çağrı yapılabilir.
05Framework, Core ve .NET 5+
Sürüm karmaşası mülakatta sık sorulur; net bir haritası vardır:
| .NET Framework (4.8.x) | .NET (Core, 5, 6, 8, 9…) | |
|---|---|---|
| Platform | Yalnız Windows | Windows · Linux · macOS |
| Sürüm modeli | Makineye kurulur, tek sürüm (yerinde güncellenir) | Yan yana (side-by-side); uygulama kendi sürümünü seçer |
| Proje dosyası | Eski, uzun .csproj; packages.config | SDK biçimli kısa .csproj; PackageReference |
| Web | System.Web / IIS'e bağlı | Kestrel; IIS/nginx ters vekil arkasında |
| Yapılandırma | web.config / app.config (XML) | appsettings.json + sağlayıcı zinciri (3. ders) |
| Durum | Bakımda; yeni özellik gelmiyor | Aktif geliştirme; yıllık sürüm |
| Performans | Sabit | Her sürümde ölçülebilir kazanç |
Adlandırma tarihi kısaca şudur: 2016'da açık kaynak ve çapraz platform olan .NET Core çıktı; 3.1'den sonra "Core" adı bırakıldı ve .NET 5 ile tek bir isim altında birleşildi (4 atlandı, çünkü .NET Framework 4.x ile karışırdı). Bugün "yeni .NET" demek .NET 5 ve sonrası demektir.
.NET Standard ise bir uygulama değil sözleşmedir: farklı .NET
uygulamalarının ortak API kümesini tanımlar ve kütüphanelerin hem Framework hem Core
tarafından kullanılabilmesini sağlardı. Bugünkü tavsiye nettir: yalnız modern .NET'i
hedefleyen yeni kütüphanelerde doğrudan net8.0 hedeflenir;
netstandard2.0 yalnız hâlâ .NET Framework'ten tüketilmesi gereken
kütüphaneler için tutulur.
Sürüm politikası: her yıl kasımda bir sürüm çıkar. Çift numaralılar LTS'tir (3 yıl destek), tek numaralılar STS (18 ay). Üretim sistemlerinde varsayılan tercih LTS'tir; ara sürüm seçmek, desteğin bitiminde yükseltmeyi zorunlu kılar. Desteği biten sürümde kalmak yalnız özellik değil güvenlik yaması kaybıdır — 14. hattaki güvenlik yönetimi dersinde bu, bilinen açıklarla çalışmak anlamına gelir.
Eski sistemi taşımak gerçek hayatta sık karşılaşılan iştir. Sıra: bağımlılık envanteri çıkarılır (Framework'e özgü olanlar: System.Web, WCF sunucu, WebForms, Remoting); .NET Upgrade Assistant ile proje dosyaları SDK biçimine çevrilir; Windows'a özgü API'ler için Windows Compatibility Pack kullanılır; taşınamayan parçalar (WebForms gibi) yeniden yazılır. 4. hattaki boğan incir yöntemi burada birebir uygulanır: sistem tek seferde değil, modül modül taşınır.
06Yayımlama biçimleri
dotnet publish ile üretilen çıktı, dağıtım kararının somut hâlidir:
dotnet publish -c Release # çerçeveye bağımlı
dotnet publish -c Release -r linux-x64 --self-contained true # kendine yeterli
dotnet publish -c Release -r linux-x64 -p:PublishSingleFile=true
dotnet publish -c Release -r linux-x64 -p:PublishReadyToRun=true
dotnet publish -c Release -r linux-x64 -p:PublishAot=true
- Çerçeveye bağımlı (framework-dependent) — küçük çıktı, sunucuda uygun çalışma zamanı kurulu olmalıdır. Kapsayıcıda taban imaj çalışma zamanını içerdiği için yaygın seçimdir.
- Kendine yeterli (self-contained) — çalışma zamanı da pakete girer; hedef makinede kurulum gerekmez, boyut büyür ve güvenlik yamaları için uygulamayı yeniden yayımlaman gerekir.
- Tek dosya — dağıtım kolaylığı; çalışma zamanında geçici klasöre açılan yerel kütüphaneler olabilir.
- Trimleme — kullanılmayan IL kırpılır, boyut düşer; yansımayla erişilen tipler kırpılabildiği için uyarıları ciddiye almak gerekir.
- NativeAOT — en hızlı başlatma, en küçük bellek; yansıma/dinamik kod kısıtları ve platforma özgü çıktı bedeliyle.
RID (Runtime Identifier: win-x64, linux-x64,
linux-musl-x64, osx-arm64) hedef platformu belirtir; Alpine
tabanlı kapsayıcılarda musl varyantının gerektiğini bilmemek, 17. hatta
sık karşılaşacağın bir hata kaynağıdır.
07Mini alıştırma — Platformu gözle
Yaklaşık 20 dakika · dotnet komut satırı
- Envanter:
dotnet --info,--list-sdks,--list-runtimesçıktısını incele. Kaç çalışma zamanı kurulu, hangileri LTS? - IL'e bak: küçük bir konsol uygulaması yaz (bir
foreach, birusing, birasyncmetot içersin).dotnet buildsonrası çıkan.dll'i bir IL çözücüde (ILSpy / dnSpy /ildasm) aç.asyncmetodun yerinde hangi sınıfı görüyorsun? - Isınmayı ölç: ağır bir döngüyü
Stopwatchile 10 kez ölç ve süreleri yazdır. İlk tur neden diğerlerinden yavaş? - Kademeli derlemeyi kapat: proje dosyasına
<TieredCompilation>false</TieredCompilation>ekleyip aynı ölçümü tekrarla. İlk tur ve sonraki turlar nasıl değişti? - Yayımlama karşılaştırması: aynı uygulamayı çerçeveye bağımlı, kendine yeterli ve ReadyToRun olarak yayımla. Üç klasörün boyutunu ve ilk çalıştırma süresini bir tabloya yaz.
- Karar: "5 saniyede bir tetiklenen, 200 ms çalışan bir zamanlanmış iş" için hangi yayımlama biçimini seçerdin? Gerekçeni iki cümleyle yaz.
08Kendini sına
C# kodu çalışana kadar hangi aşamalardan geçer?
İki aşamalı bir yol: Roslyn derleyicisi kaynağı derleme zamanında
IL'e (işlemciden bağımsız ara dil) çevirir ve bir derleme birimi
(.dll) üretir; bu dosyada IL kodu, metadata (tip ve üye tanımları) ve
manifest (ad, sürüm, bağımlılıklar) bulunur. Metadata'nın orada olması yansımayı,
serileştirmeyi ve bağımlılıkların çalışma zamanında çözülmesini mümkün kılar. Çalışma
zamanında CLR devreye girer: JIT, her metodu ilk çağrıldığında makine koduna
çevirir ve sonucu bellekte tutar; ikinci çağrı doğrudan makine kodudur. Kazanç,
çalışılan makinenin işlemci yeteneklerine göre derleyebilmek; bedeli ilk çağrının
yavaşlığı, yani ısınmadır. Modern .NET bunu kademeli derlemeyle yumuşatır: metot önce
hızlı derlenir, sıcaklaşınca arka planda yeniden iyileştirilir (uzun döngülerde OSR
ile), dinamik PGO ise sanal çağrıları profil bilgisiyle doğrudan çağrıya çevirebilir.
Önceden derleme seçenekleri de vardır: ReadyToRun makine kodunu pakete ekler ve soğuk
başlatmayı kısaltır, NativeAOT ise JIT'i tamamen kaldırır.
CLR ne yapar? "Yönetilen kod" ne demektir?
Yönetilen kod, doğrudan işletim sistemine değil çalışma zamanına
teslim edilen koddur. CLR'ın hizmetleri: bellek yönetimi (yığında ayırma ve çöp
toplama), tip güvenliği ve IL doğrulaması, istisna yönetimi ve yığın çözme, derleme
birimi yükleme ve bağımlılık çözümü, JIT derlemesi, yerel kodla birlikte çalışma
(P/Invoke). GC tarafında bilinmesi gereken ek ayrım kiplerdir: workstation GC gecikmeyi,
server GC ise çekirdek başına ayrı yığın ve toplama iş parçacığıyla verimi hedefler;
ASP.NET Core'da server GC varsayılandır ve kapsayıcıda CPU/bellek sınırlarına göre
ayarlanmalıdır. Derleme birimi yükleme eski .NET Framework'te AppDomain ve makine
genelindeki GAC ile yapılırdı; modern .NET'te AssemblyLoadContext
kullanılır ve bağımlılıklar uygulama klasöründeki .deps.json'a göre
çözülür.
.NET Framework ile .NET (Core / 5+) arasındaki farklar neler? .NET Standard nerede kalır?
Framework yalnız Windows'ta çalışır, makineye tek sürüm olarak
kurulur ve yerinde güncellenir; web tarafı System.Web/IIS'e bağlıdır, yapılandırma XML
tabanlıdır, bugün bakım modundadır. Modern .NET çapraz platformdur, sürümler yan yana
durur ve uygulama kendi sürümünü seçer; SDK biçimli kısa proje dosyası ve
PackageReference kullanır, web tarafında Kestrel çalışır, yapılandırma
sağlayıcı zinciriyle yapılır ve her sürümde ölçülebilir performans kazancı gelir. Ad
tarihi: .NET Core 3.1'den sonra "Core" düşürüldü, .NET 5 ile tek isimde birleşildi;
4 numarası Framework 4.x ile karışmamak için atlandı. .NET Standard bir uygulama değil
sözleşmedir — farklı .NET uygulamalarının ortak API kümesini tanımlar; bugün yeni
kütüphanelerde doğrudan net8.0 hedeflenir, netstandard2.0
yalnız .NET Framework'ten de tüketilecek kütüphaneler için tutulur. Sürüm politikası:
yılda bir sürüm, çift numaralar LTS (3 yıl), tek numaralar STS (18 ay); üretimde
varsayılan tercih LTS'tir çünkü destek bitimi güvenlik yaması kaybı demektir.
Çerçeveye bağımlı, kendine yeterli, ReadyToRun ve NativeAOT yayımlama arasında nasıl seçim yaparsın?
Çerçeveye bağımlı yayımlama en küçük çıktıyı verir ama hedefte
uygun çalışma zamanının kurulu olmasını gerektirir; kapsayıcıda taban imaj çalışma
zamanını içerdiği için yaygın seçimdir ve güvenlik yaması taban imaj güncellenerek
gelir. Kendine yeterli yayımlama çalışma zamanını da pakete koyar — hedefte kurulum
gerekmez, ama boyut büyür ve her yama için uygulamayı yeniden yayımlamak gerekir. Tek
dosya dağıtımı kolaylaştırır. Trimleme boyutu düşürür ama yansımayla erişilen tipleri
kırpabilir, uyarıları ciddiye almak gerekir. ReadyToRun makine kodunu pakete ekleyerek
soğuk başlatmayı kısaltır, karşılığında dosya büyür. NativeAOT JIT'i tamamen kaldırır:
başlatma milisaniyeler düzeyine iner ve bellek ayak izi küçülür, ama yansıma ve dinamik
kod üretimi kısıtlanır ve çıktı tek platform/mimariye özgüdür — sunucusuz işlevler ve
komut satırı araçları gibi kısa ömürlü, sık başlayan işlerde değerlidir, klasik bir web
API'de genelde gerekmez. Hedef platform RID ile belirtilir; Alpine tabanlı imajlarda
musl varyantı gerekir.
09Sınav köşesi
Yazılıda nasıl sorulur
(1) Kısaltma: CLR, IL/CIL, JIT, BCL, GC, CTS/CLS açılımları ve görevleri. (2) Süreç: "C# kodu nasıl çalışır, aşamaları yazınız" — IL ve JIT'in ikisi de geçmelidir. (3) Karşılaştırma: .NET Framework ile .NET Core/5+ farkları (platform, sürüm modeli, web yığını, proje dosyası). (4) Kavram: yönetilen/yönetilmeyen kod; assembly nedir, içinde ne bulunur; metadata ne işe yarar. (5) .NET Standard nedir, bugün ne zaman hedeflenir. (6) Sürüm: LTS/STS farkı ve destek süreleri. (7) Yayımlama: framework-dependent / self-contained / single-file / trimming / AOT ayrımı ve RID kavramı. (8) Performans: tiered compilation ve ReadyToRun'ın soğuk başlatmaya etkisi; server GC ile workstation GC farkı.
Sözlüde nasıl anlatılır — 2 dakikalık taslak
Düzen: (1) Tanım — ".NET bir platformdur: diller, Roslyn derleyicisi, CLR çalışma zamanı, temel sınıf kütüphanesi ve SDK araçları." (2) Akış — "C# önce IL'e derlenir, IL metadata'yla birlikte assembly'de durur; çalışma zamanında JIT her metodu ilk çağrıda makine koduna çevirir. Bu yüzden ilk istekler yavaştır, kademeli derleme ve ReadyToRun bunu yumuşatır." (3) CLR — "bellek yönetimi, tip güvenliği, istisna yönetimi ve assembly yükleme çalışma zamanının işidir; bu yüzden koda 'yönetilen kod' denir." (4) Sürümler — "Framework yalnız Windows'ta ve bakım modunda; modern .NET çapraz platform, yan yana sürümlenebilir ve yılda bir çıkıyor. Üretimde LTS seçerim." (5) Kıdem vuruşu — "dağıtım kararını iş yüküne göre veririm: kapsayıcıda çerçeveye bağımlı yayımlar, taban imajla yama alırım; çok sık başlayan kısa ömürlü işlerde NativeAOT'u değerlendiririm ama yansıma kısıtını göz önünde tutarım. Kapsayıcıda ayrıca GC kipini ve CPU sınırlarını kontrol ederim — server GC varsayılan gelir ve sınırlı CPU'da beklenmedik bellek davranışı üretebilir."
