【.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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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…