Skip to content

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 — dotnet komut 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 (unsafe bloğu bilinçli bir istisnadır).
  • İstisna yönetimi — yığın çözme (stack unwinding) ve try/catch/finally semantiğ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 AssemblyLoadContext vardır ve bağımlılıklar uygulama klasöründeki .deps.json dosyasına göre çözülür.
  • Birlikte çalışma — yerel (native) kütüphanelere P/Invoke ile ç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…)
PlatformYalnız WindowsWindows · Linux · macOS
Sürüm modeliMakineye 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.configSDK biçimli kısa .csproj; PackageReference
WebSystem.Web / IIS'e bağlıKestrel; IIS/nginx ters vekil arkasında
Yapılandırmaweb.config / app.config (XML)appsettings.json + sağlayıcı zinciri (3. ders)
DurumBakımda; yeni özellik gelmiyorAktif geliştirme; yıllık sürüm
PerformansSabitHer 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ı

  1. Envanter: dotnet --info, --list-sdks, --list-runtimes çıktısını incele. Kaç çalışma zamanı kurulu, hangileri LTS?
  2. IL'e bak: küçük bir konsol uygulaması yaz (bir foreach, bir using, bir async metot içersin). dotnet build sonrası çıkan .dll'i bir IL çözücüde (ILSpy / dnSpy / ildasm) aç. async metodun yerinde hangi sınıfı görüyorsun?
  3. Isınmayı ölç: ağır bir döngüyü Stopwatch ile 10 kez ölç ve süreleri yazdır. İlk tur neden diğerlerinden yavaş?
  4. Kademeli derlemeyi kapat: proje dosyasına <TieredCompilation>false</TieredCompilation> ekleyip aynı ölçümü tekrarla. İlk tur ve sonraki turlar nasıl değişti?
  5. 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.
  6. 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."