【.Net技術棧梳理】01-核心框架與運行時(CLR)

文章目錄

  • 1 .NET Runtime(CLR-公共語言運行時)
    • 1.1 中間語言 IL
      • 1.1.1 從源代碼到通用中間語言(IL)
      • 1.1.2 運行時加載:CLR登場
      • 1.1.3 核心步驟:即時編譯 (JIT Compilation)
      • 1.1.4 執行與內存管理(GC)
      • 1.1.5 演進與高級模式:分層編譯與 AOT
    • 1.2 CLR運行原理
      • 1.2.1 從源代碼到程序集:一切的開端
      • 1.2.2 運行時加載與初始化
      • 1.2.3 JIT 編譯:從通用 IL 到原生代碼
      • 1.2.4 執行與管理:CLR 的持續服務
      • 1.2.5 演進與高級模式

核心框架與運行時是.NET的基石,決定了能開發什么類型的應用已經如何運行。

1 .NET Runtime(CLR-公共語言運行時)

負責執行編譯后的代碼(中間語言,IL)、內存管理(垃圾回收GC)、異常處理、線程管理等。是所有.Net應用的引擎

1.1 中間語言 IL

.Net運行時(CLR)執行編譯后的代碼(中間語言,IL)是一個核心過程,理解這個過程就能明白跨平臺、安全性、高性能等特性的基礎。

整個過程可以概括為一下關鍵階段

  1. 編寫源代碼與編譯為IL
  2. 分發與部署(包含IL的程序集)
  3. 運行時加載與即時編譯(JIT Compilation)
  4. 執行本地代碼
  5. 優化與高級特性(分層編譯、AOT)

1.1.1 從源代碼到通用中間語言(IL)

當使用 C#、F# 或 VB.NET 編寫代碼并執行 dotnet build 時,發生的事情與 C/C++ 這樣的原生語言完全不同

  • C/C++ (原生編譯):編譯器直接將源代碼編譯為針對特定 CPU 架構(如 x86, ARM)和操作系統的本地機器碼。這個代碼無法在其他平臺上運行
  • .NET (托管編譯):編譯器(如 Roslyn for C#)會將源代碼編譯為一種稱為 中間語言 (IL) 或 通用中間語言 (CIL) 的字節碼。同時,它還會生成豐富的元數據(描述代碼中的類型、成員、引用等信息)
  • IL是什么

可以把IL想象成一種高度抽象、與特定CPU無關的“匯編語言”。它比高級語言更底層,但是比真正的機器碼更高級。它包含了ldloc(加載本地變量)、add(相加)、call(調用方法)這樣的指令

  • 為什么這樣做?

關鍵優勢:跨平臺和語言互操作性。IL是一種統一的、標準的輸出格式。無論使用的事C#還是F#,最終都變成了IL。這使得.NET運行時只需要理解IL這一種語言,就能運行所有.NET語言編寫的程序。同時,因為IL不是特定于某個平臺的,所以同一個IL程序集(.dll或.exe)可以分發到任何有相應.NET運行時(CLR)的平臺上(Windows、Linux、macOS)

1.1.2 運行時加載:CLR登場

當運行一個.NET程序時,操作系統會啟動.NET運行時(CLR)。CLR的程序集加載器會負責找到并加載程序集(以及它所依賴的所有程序集)。加載后,CLR會讀取其中的元數據和IL代碼,為執行做準備。

1.1.3 核心步驟:即時編譯 (JIT Compilation)

這是最神奇、最核心的一步,CLR不會直接“解釋”執行IL(像早期的Java或Python那樣)。相反,它使用一個名為JIT編譯器(Just-In-Time Compiler)的組件

JIT編譯器的工作流程如下:

  1. 按需編譯:當一個方法(函數)第一次被調用時,JIT編譯器才會開始工作。CLR不會在程序啟動時就把所有IL都編譯成本地代碼,這避免了不必要的啟動延遲。
  2. 讀取IL:JIT編譯器從已加載的程序集中獲取該方法的IL代碼。
  3. 驗證:在編譯之前,JIT會執行一個重要的驗證過程。它會檢查IL代碼是否是類型安全的(例如:不會錯誤地將一個整數當做對象引用來使用)。這個步驟是.NET內存安全和安全沙箱的基石,它能組織大量潛在的內存損壞漏洞。
  4. 編譯為本地代碼:驗證通過后,JIT編譯器將IL代碼動態地編譯成當前所在平臺本地機器碼(x86、x64、ARM等)。這個過程考慮了當前的CPU和操作系統環境。
  5. 存儲和執行:編譯生成的本地機器碼被存儲在內存中的一塊特定的區域(通常稱為JIT代碼堆)。然后CLR修改該方法的方法表,使其條目指向這塊新生成的本地代碼。最后,程序執行這個剛剛編譯好的、極其高效的本地代碼。

JIT的優勢

  • 跨平臺
    同一個IL包,在Windows上JIT編譯為x86代碼,在Linux上編譯為x64代碼,在Raspberry Pi上編譯為ARM代碼。

  • 性能優化
    JIT編譯器可以進行運行時優化。它可以根據程序運行的實際環境進行優化。例如,如果它檢測到運行程序的CPU支持特定的指令集(如AVX2),它就可以生成使用這些指令的更高效的代碼。靜態編譯器(如C++)在編譯時無法知道程序最終會運行在什么CPU上,因此無法做到這一點。

  • 節省內存
    只有真正被執行到的代碼才會被編譯和加載到內存中。

1.1.4 執行與內存管理(GC)

代碼已經是以本地機器碼的形式在 CPU 上直接執行了,速度非常快。
在執行過程中,CLR 的另一個核心組件——垃圾回收器 (Garbage Collector, GC)——會持續工作。它負責自動分配和釋放內存。當對象不再被引用時,GC 會自動回收它們占用的內存,開發者無需(也不能)手動釋放。這消除了內存泄漏和懸空指針等常見問題。

1.1.5 演進與高級模式:分層編譯與 AOT

最初的 JIT 編譯策略是“一次性編譯”,但現代 .NET(.NET Core 3.0+)引入了更先進的策略:

  1. 分層編譯 (Tiered Compilation)
  • 第一層 (快速 JIT):當一個方法第一次被調用時,JIT會快速地進行編譯,生成優化程度較低但編譯速度極快的代碼。目標是盡快讓程序跑起來
  • 第二層 (優化 JIT):如果發現某個方法被頻繁調用(成為“熱路徑”),CLR會在后臺異步地啟動一個優化版本的JIT編譯器,重新編譯該方法,生成高度優化的、更快的本地代碼。之后對該方法的調用就會切換到優化版本上。
  • **好處:**完美平衡了啟動速度和運行速度。
  1. 預先編譯 (AOT - Ahead of Time)
  • 雖然JIT很棒,但是它的編譯過程仍然會在程序運行時產生一些開銷(CPU和內存)。對于某些場景(如啟動速度極致的App、命令工具),我們希望消除這個開銷
  • Native AOT:.NET提供了Native AOT編譯模式。它在發布時就直接將IL代碼編譯為本地可執行文件,完全不需要在目標機器上安裝.NET運行時,也沒有JIT編譯階段
  • 結果:生成的文件更大,啟動速度極快,但失去了JIT的運行時優化能力。.NET 8和更高版本對Native AOT的支持已經非常完善

總結與類比

步驟.Net(托管)Java傳統原生(C/C++)
編譯源代碼 -> 中間語言 (IL)+ 元數據源代碼 -> 字節碼 (.class)源代碼 -> 本地機器碼 (.exe)
分發包含 IL 的程序集(跨平臺)包含字節碼的 JAR 文件(跨平臺)特定平臺的二進制文件
執行CLR + JIT 編譯為本地代碼并執行JVM + JIT 編譯為本地代碼并執行操作系統直接加載執行

可以把一個 .NET 程序想象成:

  • IL 是一份標準化的、與烹飪設備無關的菜譜
  • CLR 是一位廚師(JIT 編譯器)和一個廚房(運行時環境)
  • 廚師在接到訂單(方法調用)時,根據手頭的廚具(CPU 架構)和食材(環境),現場(Just-In-Time) 將菜譜翻譯成具體的烹飪步驟(本地機器碼)并做菜(執行)。

這種方式既保證了菜譜(程序)的通用性,又能讓每位廚師(不同平臺上的 CLR)利用自己廚房的最優條件做出最好的菜。

1.2 CLR運行原理

CLR 是一個復雜的執行環境,它的核心任務是管理 .NET 代碼的執行,提供內存管理、線程管理、類型安全、異常處理、安全性等一系列關鍵服務。我們可以將其運行原理分解為以下幾個核心階段和組件:

1.2.1 從源代碼到程序集:一切的開端

運行程序之前,旅程早已開始:

  1. 編譯(Compiler):用 C#、F# 或 VB.NET 等高級語言編寫代碼。編譯器(如 C# 的 Roslyn)會將其編譯為 中間語言(IL / CIL) 和元數據(Metadata),并打包成一個 程序集(Assembly)(通常是 .dll 或 .exe 文件)。
    • IL:一種與特定 CPU 無關的、類似匯編的指令集。它比高級語言低級,但比原生機器碼高級。它是 CLR 的“通用語言”。
    • 元數據:一個詳細的“清單”,描述了程序集中的所有類型、方法、屬性、依賴關系等。這使得 CLR 和開發工具能夠智能地理解代碼結構。
  2. 部署(Deployment):分發這個包含 IL 和元數據的程序集。它的一個關鍵優勢是跨平臺性——同一個 IL 包可以在任何有對應 .NET 運行時(Windows, Linux, macOS)的平臺上運行。

1.2.2 運行時加載與初始化

雙擊一個 .NET.exe 或在命令行中啟動它時,操作系統的加載器會識別出這是一個 .NET 程序,并啟動相應的CLR

  1. 啟動 CLR:操作系統加載 coreclr.dll(對于 .NET Core/.NET 5+)或 clr.dll(對于 .NET Framework),這是 CLR 本身的實現。

  2. 創建應用程序域(AppDomain):CLR 會為應用程序創建一個邏輯隔離容器,稱為應用程序域。它提供了代碼加載、執行和卸載的隔離邊界(雖然在 .NET Core 中 AppDomain 的隔離性被削弱,概念依然存在)。

  3. 加載程序集:CLR 的程序集加載器(Loader) 找到你的啟動程序集(EXE)及其所有依賴項(DLLs),并將它們加載到應用程序域中。它使用元數據來解析所有依賴關系。

1.2.3 JIT 編譯:從通用 IL 到原生代碼

這是 CLR 最核心、最精妙的部分。CLR 并不直接解釋執行 IL,而是采用了一種稱為 即時編譯(Just-In-Time Compilation, JIT) 的技術。

  1. 按需編譯:當一個方法(函數)第一次被調用時,CLR 才會介入。

  2. 驗證(Verification):在編譯之前,JIT 編譯器會執行一個至關重要的步驟——驗證。它會分析該方法的 IL 代碼,確保它是類型安全的(例如,不會將整數當作對象引用來錯誤使用、不會發生緩沖區溢出)。這是 .NET 內存安全和穩定性的基石,它阻止了絕大多數常見的安全漏洞和程序崩潰。

  3. 編譯為原生代碼:驗證通過后,JIT 編譯器將 IL 代碼動態地編譯成當前運行機器的特定 CPU 架構(x86, x64, ARM)的生機器碼

  4. 存儲與執行:編譯好的原生代碼被存儲在內存中的一塊特定區域(JIT 代碼堆)。CLR 然后會修補該方法的方法表,使其條目指向這塊新生成的、高效的本地代碼。最后,程序執行這個剛剛編譯好的本地代碼

JIT 的優勢

  • 跨平臺:一份 IL,處處編譯運行。

  • 性能優化:JIT 可以進行運行時優化。它知道程序運行的具體硬件環境(CPU 型號、指令集支持),可以生成最適合當前機器的優化代碼。這是靜態編譯器(如 C++)無法做到的。

  • 分析引導的優化(PGO):現代 .NET 的 JIT 甚至可以觀察程序的運行 profile(哪些分支最常走?哪些方法是熱路徑?),并進行更激進的優化。

1.2.4 執行與管理:CLR 的持續服務

在代碼執行過程中,CLR 持續提供關鍵服務,就像一個全能的管家:

  1. 內存管理與垃圾回收(GC)

    • 分配:當使用 new 關鍵字創建對象時,CLR 在托管堆上為其分配內存。托管堆是一塊由 CLR 連續管理的內存區域,分配速度極快(僅需移動一個指針)。

    • 回收垃圾回收器(Garbage Collector) 會自動追蹤對象的引用。當堆內存不足時,GC 會啟動,它從“根對象”開始遍歷,標記所有仍在被引用的存活對象,然后回收未被標記的垃圾對象的內存。之后,它會壓縮存活對象,消除內存碎片。這個過程完全是自動的,開發者無需關心。

  2. 異常處理

    • CLR 提供了一套結構化的、跨語言的異常處理機制。當異常被拋出時,CLR 會中斷當前流程,遍歷調用棧,尋找合適的 catch 塊來處理異常。如果找不到,則終止進程。
  3. 線程管理

    • CLR 提供了線程池(ThreadPool),高效地管理線程的生命周期,避免頻繁創建和銷毀線程的巨大開銷。它也是 async/await 異步編程模型的底層支撐。
  4. 安全性

    • .NET 提供了基于證據的安全性(如代碼的來源、出版商),雖然現在較少使用,但其驗證系統本身就是一道強大的安全防線,阻止不安全的代碼執行。
  5. 互操作性

    • 通過 P/Invoke(平臺調用),CLR 允許托管代碼調用原生 C/C++ 編寫的庫(DLLs)。

    • 通過 COM Interop,允許 .NET 與傳統的 COM 組件進行交互。

1.2.5 演進與高級模式

CLR 也在不斷進化,引入了新的編譯模式以適應更多場景:

  • ReadyToRun (R2R):一種預先編譯(Ahead-Of-Time, AOT) 的形式。程序集在發布時就被部分編譯為本機代碼,減少了應用程序啟動時的 JIT 編譯開銷,從而改善啟動性能。它更像是“JIT 預熱”的產物。
  • Native AOT:(.NET 7/8+ 的重點)程序在發布時被完全編譯為一個獨立的、不依賴 .NET 運行時的原生可執行文件。沒有 JIT,沒有 IL。代價是失去了 JIT 的運行時優化能力,并且文件更大,但換來了極致的啟動速度和更小的部署體積(只包含真正用到的代碼)。這是創建獨立命令行工具和資源受限環境的理想選擇。

在這里插入圖片描述

CLR 的精妙之處在于,它通過 JIT 編譯托管環境,在開發效率(自動內存管理、跨平臺、類型安全)、執行性能(JIT 優化、高效內存分配)和安全性之間取得了非凡的平衡。它讓開發者能從繁瑣的底層細節中解放出來,專注于實現業務邏輯。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/922048.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/922048.shtml
英文地址,請注明出處:http://en.pswp.cn/news/922048.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Claude Code 平替:OpenAI發布 Codex CLI ,GPT-5 國內直接使用

openai推出的命令行編程工具codex已經可以使用最新 GPT-5 模型,擁有可媲美 Claude Code 的 AI 編碼能力。本文將指導你在 Windows 系統上部署原生的 Codex CLI 程序,并接入超低價中轉 API,讓你在國內直接用上超高性價比的 OpenAI Codex CLI 應…

在VS2022的WPF仿真,為什么在XAML實時預覽點擊 ce.xaml頁面控件,卻不會自動跳轉到具體代碼,這樣不方便我修改代碼,

在VS2022的WPF仿真,為什么在XAML實時預覽點擊 WpfApp1\FunctionalModule\08Replace\Replace.xaml頁面控件,卻不會自動跳轉到具體代碼,這樣不方便我修改代碼,

Git Bash 別名

有些常用的指令參數非常多,每次都要輸入好多參數,我們可以使用別名。Linux 系統中很多 shell,包括 bash,sh,zsh,dash 和 korn 等,不管哪種 shell 都會有一個 .bashrc 的隱藏文件,它就…

Centos7部署ceph存儲

一、準備5臺centos7主機 node節點雙網卡&#xff08;1個內部檢測&#xff0c;1個外部使用&#xff09;node節點都添加新網卡關閉防火墻和上下文都需要添加hosts文件都需要cat > /etc/hosts << EOF > 127.0.0.1 localhost localhost.localdomain localhost4 loca…

2025.9.10總結

今日感悟&#xff1a;刷到00后下班去菜市場撿菜的熱點視頻&#xff0c;確實挺有意思&#xff0c;不得不說&#xff0c;又省錢又好玩。雖然每天晚上能免費領個25塊錢的水果回去&#xff0c;但確實沒有什么新鮮感了。別人下班還能撿撿菜放松下&#xff0c;我下班&#xff0c;除了…

【數據結構與算符Trip第2站】稀疏數組

稀疏sparsearray數組 什么是稀疏數組&#xff1f; 稀疏數組是一種特殊的數據結構&#xff0c;用于高效存儲和表示大部分元素為零&#xff08;或默認值&#xff09;的數組。它通過只存儲非零元素的位置和值來節省內存空間。是一種壓縮數組。 實現原理 在Go語言中&#xff0c;稀疏…

Sub-GHz無線收發單片機,低功耗物聯網通信的硬件“基石”

隨著物聯網應用持續向規模化部署、廣域化覆蓋與高效化協同邁進&#xff0c; 作為IoT終端設備實現無線交互的核心通信單元之一——Sub-GHz無線收發單片機&#xff08;Sub-GHz射頻收發芯片與單片機高度集成&#xff09;已成為系統設計中進一步簡化外圍元件數量、縮小硬件體積、降…

用Typescript 的方式封裝Vue3的表單綁定,支持防抖等功能

在 Vue3 中結合 TypeScript 封裝表單綁定方案時&#xff0c;需要綜合考慮類型安全、功能擴展性和開發體驗。以下是一個包含防抖功能、支持多種表單控件、具備完整類型推導的封裝方案&#xff0c;全文約 2300 字&#xff1a; 方案設計思路 組合式函數封裝&#xff1a;使用 Vue3 …

中悅大華通過訂單日記實現流程重構之路

一、客戶背景 安徽中悅大華高速流體機械有限公司&#xff0c;成立于2023年&#xff0c;位于安徽省宣城市&#xff0c;是一家以從事電子設備制造為主的企業&#xff0c;在多年的商業經營中已成為業界翹楚。 在業務不斷壯大的過程中&#xff0c;面臨生產協作效率低&#xff0c;庫…

【Springboot】介紹啟動類和啟動過程

【Springboot】介紹啟動類和啟動過程【一】Spring Boot 啟動類的注解【1】核心注解&#xff1a;SpringBootApplication&#xff08;1&#xff09;?SpringBootConfiguration?&#xff1a;Spring容器會從該類中加載Bean定義&#xff08;2&#xff09;?EnableAutoConfiguration…

Gears實測室:第一期·音游跨設備性能表現與工具價值實踐

在音游品類中&#xff0c;《跳舞的線》以 “音樂與操作節奏深度綁定” 的玩法特性&#xff0c;對設備性能提出了特殊要求 —— 穩定的幀率與低延遲的渲染響應&#xff0c;直接影響玩家對音符時機的判斷&#xff0c;一旦出現卡頓或幀波動&#xff0c;易導致操作失誤&#xff0c;…

格式刷+快捷鍵:Excel和WPS表格隔行填充顏色超方便

有時候我們會對Excel或WPS表格的數據區域每隔一行填充一個底紋&#xff0c;便于閱讀和查看。可以使用條件格式搭配公式實現&#xff0c;也可以手動設置。通常手動設置的時候是先設置一行&#xff0c;然后再雙擊格式刷應用。可以有更快的方式&#xff1a;先設置一行底紋&#xf…

將現有Spring Boot項目作為模塊導入到另一個Spring Boot項目

將現有Spring Boot項目作為模塊導入到另一個Spring Boot項目的操作步驟如下&#xff1a;?項目結構調整?將待導入的項目文件夾復制到主項目的根目錄下修改子模塊目錄名保持命名規范&#xff08;如ms-xxx格式&#xff09;?父POM配置?在主項目的pom.xml中添加<modules>聲…

激光頻率梳 3D 輪廓測量 - 銑刀刀片的刀口鈍化值 R 的測量

一、引言銑刀刀片的刀口鈍化值 R 是影響切削性能的關鍵參數&#xff0c;其精度直接關系到工件表面質量與刀具壽命。傳統測量方法在面對微米級鈍化圓角時存在分辨率不足、接觸式測量易損傷刃口等問題。激光頻率梳 3D 輪廓測量技術憑借飛秒級時頻基準與亞微米級測量精度&#xff…

3-10〔OSCP ? 研記〕? WEB應用攻擊?XSS攻擊理論基礎

鄭重聲明&#xff1a; 本文所有安全知識與技術&#xff0c;僅用于探討、研究及學習&#xff0c;嚴禁用于違反國家法律法規的非法活動。對于因不當使用相關內容造成的任何損失或法律責任&#xff0c;本人不承擔任何責任。 如需轉載&#xff0c;請注明出處且不得用于商業盈利。 …

《嵌入式硬件(四):溫度傳感器DS1820》

一、DS1820的引腳DS1820單總線數字溫度計&#xff1a;異步串行半雙工特性&#xff1a;1&#xff09;獨特的單線接口&#xff0c;只需 1 個接口引腳即可通信2&#xff09;多點&#xff08;multidrop&#xff09;能力使分布式溫度檢測應用得以簡化3&#xff09;不需要外部元件4&a…

langchain 輸出解析器 Output Parser

示例中使用的公共代碼&#xff1a; from langchain_deepseek import ChatDeepSeek chat ChatDeepSeek(model"deepseek-chat",temperature0,api_keyAPI_KEY, )使用方法&#xff1a; 引入解析器實例化解析器調用解析器的get_format_instructions()獲得提示詞&#xff…

LeetCode算法日記 - Day 37: 驗證棧序列、N叉樹的層序遍歷

目錄 1. 驗證棧序列 1.1 題目解析 1.2 解法 1.3 代碼實現 2. N叉樹的層序遍歷 2.1 題目解析 2.2 解法 2.3 代碼實現 1. 驗證棧序列 https://leetcode.cn/problems/validate-stack-sequences/description/ 給定 pushed 和 popped 兩個序列&#xff0c;每個序列中的 值…

金融數據庫--3Baostock

一、 Baostock 是什么&#xff1f;Baostock&#xff08;寶碩股票&#xff09;是一個免費、開源的證券數據平臺&#xff08;SDK&#xff09;&#xff0c;旨在為金融量化投資者、研究人員和學生提供穩定、準確、易用的A股歷史數據和相關金融數據。其核心是一個 Python 庫&#xf…

微信小程序-1-微信開發者工具環境搭建和初始化創建項目

文章目錄1 小程序概述1.1 什么是微信小程序1.2 大前端概念1.3 賬號注冊1.4 開發流程1.5 小程序成員2 創建項目2.1 創建項目流程2.2 創建項目2.3 本地開發支持http3 項目目錄3.1 項目目錄結構3.2 配置文件3.2.1 app.json(全局配置)3.2.2 xxx.json(頁面配置)3.2.3 project.config…