前言
万事开头难,很早之前就想写博客记录些东西,迟迟未行动,甚是遗憾。原因诸多,大体上无非都是懒、没意志力等等。这次从自己的读书笔记开始,兴许能够有所改变。
一、CLR概念
CLR(Common Language Runtime,译为公共语言运行时)是一个可由多种编程语言使用的“运行时”。CLR的核心功能:内存管理、程序集加载、安全性、异常处理和线程同步,可由面向CLR的所有语言(C++、C#、VB、F#等等)使用。无论哪种语言,最后通过编译器的结果都是托管模块。托管模块是PE32文件或PE32+文件,都需要CLR才能执行。
组成部分 | 说明 |
PE32或PE32+头 | 标准windows PE文件头,如果这个头是PE32格式,文件能在Windows32位和64位上运行,如果是PE64,文件只能在Windows的64位上运行。这个头还表示了文件类型,,并包含一个时间标记来指出文件的生成时间。对于只包含IL代码的模块,这些信息大部分都会被忽略。如果是包含本机CPU代码的模块,这个头包含本机CPU代码有关的信息。 |
CLR头 | 包含使这个模块成为托管模块的信息。头中包含要求的CLR版本,一些标志(flag),拖过模块入口方法的元数据token以及模块的元数据、资源,强模块、一些标志以及不太重要的数据项位置/大小 |
元数据 |
每个托管模块都包含元数据表。分为两类: 1、描述源代码中定义的类型和成员。 2、描述源代码引用的类型和成员。 元数据有诸多用途,例如: 1、元数据表面了编译时对原生C/C++和库文件的需求,因为在实现类型/成员的IL代码文件中,已包含有关引用类型/成员的全部信息。编译器直接从托管模块中读取元数据。 2、IDE的“智能感知”能够解析元数据,从而得到一个类型所提供的方法、属性、事件和字段。 3、CLR的代码验证过程使用元数据确保代码只执行“类型安全”的操作 4、元数据允许将对象的字段序列化到内存,将其发送给另一台机器,然后反序列化,在远程机器上重建对象状态 5、元数据允许垃圾回收器跟踪对象生存期,垃圾回收器能判断任何对象的类型并从元数据知道那个对象中的哪些字段引用了其他对象 |
IL代码 | IL代码也被为托管代码,因为CLR管理它到底执行。编译器编译源代码时生成的代码。在运行,CLR将IL编译成本机CPU指令 |
二、程序集执行过程
IL代码是与CPU无关的机器语言,当方法执行时,必须把方法的IL转换成本机CPU命令。下图是方法的首次调用
在执行Main方法前,CLR会分配一个内部数据结构来管理对引用类型的访问,在这个内部数据结构项中,存在与类型中方法对应的一个记录项,这个记录项含有一个地址,可根据此地址找到方法的具体实现。数据结构初始化时,CLR将每个记录项都设置成包含在CLR内部的JITComplier。上图中的WriteLien首次调用时,JITCompiler会被调用,IL将被编译成CPU指令(JIT,即时编译)。当WriteLine第二次调用时,会跳过JITCompiler,直接执行CPU中已有的指令。(注:之前有经常遇到过首次调用某类方法明显比第二次的耗时长,应该与此有关联,因此有时候需要做预加热)。 下图为第二次调用
生成程序集时,有两种模式:Debug和Release。显然,根据词义可知分别是调试模式和发布模式。两者主要区别在于两个C#编译器开关会影响代码优化:/optimize和/debug。Debug含有DEBUG常量,以及不优化代码。Release不包含DEBUG常量,并优化代码。因此Release模式下的程序集相对而言更小,运行更快。
尽管微软做了大量的工作来优化,但运行时的二次编译仍会影响性能,也会分配动态内存。但是.NET FrameWork SDK 配套提供了NGen.exe工具,该工具将所有IL代码编译成本机代码,并将这些本机代码保存到了一个磁盘文件中。NGen.exe能够:1、提高应用程序的启动速度;2、减小应用程序的工作集。纵然NGen.exe工具能够带来不少好处,却并非那么完美,存在如下问题:1、没有知识产权保护 2、NGen生成的文件可能失去同步 3、交叉的执行时性能。因此,在使用NGen.exe工具时要慎重考虑,除非是启动耗时的大型客户端应用程序。
三、Framework类库
.Net Framework包含Framework类库(Framework Class Library,FCL)。FCL是一组DLL程序集的同城,其中包含有数千个类型定义。例如:WEB服务、基于HTML的web窗体/MVC应用程序、“富”WIndows GUI应用程序,Windows控制台应用程序,Windows服务,数据库存储过程,组件库等
四、通用类型系统和公共语言规范
为了令一种编程语言写的代码能与用另一种编程语言写的代码沟通,微软制定了一个正式的规范来描述类型的定义和行为,这既是“通用类型系统”(Common Type System,CTS)。CTS规定,一个类型可以包含零个以上的成员:字段、方法、属性、事件,CTS还指定类型可见性规则以及类型成员的额访问规则:private、family(protected,派生类型访问)、assembly(同一个程序可访问)、public。同时,CTS规定所有类型必须继承System.Object。CTS还有其他规则,不一一列出。
微软定义了“公共语言规范”(Common Language Specification,CLS),详细定义了一个最小功能集,用于不同语言创建的对象可通过COM相互通信,任何支持CLR的编译器都支持这个功能集。
注:程序集执行过程图来源自网络