什么是CLR?
CLR,公共语言运行时(Common Language Runtime)是一个由多种语言使用的“运行时”。他的核心功能包括(内存管理、程序集加载、安全性、异常处理和线程同步),可以被面向CLR的所有语言使用。这里的“运行时”,就是一个运行时环境,就像JAVA虚拟机一样。哦,错了,确切的说是JRE(Java Runtime Enviromental)。JVM确切的说不是一个实体的java虚拟机,而是一个规范,就像CLI一样。会有不同的实现,如:JRockit还是Hotspot(前者是Oracle的JVM商业实现,后者是Sun的开源实现——当然现在也是Oracle的)
什么是CLI?
公共语言基础结构(Common Language Infrastructure),定义了构成.NET Framework基础结构的可执行代码,以及代码运行时的环境规范。它定义了一个与语言无关的跨体系结构的运行环境,这使得开发者可以用规范内定义的各种高级语言来开发软件,并且无须修正即可将软件运行在不同的计算机体系结构上。CLI是一个开放型的技术规范,由微软、惠普和英特尔于2000年向ECMA倡议的。说白了,CLI就是一套规范,CLR是对CLI的一种实现。那么CLI这份规范中具体定义了哪些内容呢?不妨去ECMA的网站下一份看看,也可以在这里下载:
如上面截图所示:包含了6部分,:
Partition I: Concepts and Architecture –描述.NET CLI的全部体系结构,提供公共类型系统(CTS,Common Type System)、虚拟执行系统(VES,Virtual Execution System)和公共语言规范(CLS,Common Language Specification)的标准化描述,还提供对元数据(Metadata)的信息性描述。
通用类型系统(CTS):规范.NET中数据的类型。
元数据系统(Metadata):是.NET中描述数据的数据。
通用语言规范(CLS):描述多语言之间进行交互的语言规范,.NET系统包括的语言有C#、C++、VB、J#,它们都遵守通用语言规范。
虚拟执行系统(VES):是一个可运行受管理代码(Managed Code)的运行环境,它提供了运行受管理代码所需要的内置数据类型(data type),以及假定的机器型态与状态设置、流程控制与例外处理等参数。
Partition II: Metadata Definition and Semantics - 提供元数据的标准化描述,包括元数据在.NET扩展PE文件格式中的位置(以.NET扩展PE文件格式的形式表示),元数据的逻辑内容(以表格及其关联的集合的形式表示,实际上使用了形式化方法表示)和元数据的语义(以汇编成为虚拟机代码的汇编器ilasm理解的形式表示)。
Partition III: CIL Instruction Set –描述CIL的指令集(注意,是CIL,不是CLI哦)
Partition IV: Profiles and Libraries- 提供CLI库的简要介绍,以及将其分解为Profile和库的规范。这里有一个配套的文件CLILibraryTypes.xml,考虑过随这一部分一起发布,不过该文件是XML格式的,该文件提供了CLI库中每一个类、值类型和接口的细节说明。“Profile”一词在这里的含义是库的集合,一起组合起来构成提供一定功能级别的一致性整体,换而言之,不同的Profile对应不同的库集合,提供的功能级别也不同。
Partition V: Debug Interchange Format- 描述了CLI 产品的调试交互的标志方式;
Partition VI: Annexes- 提供了一些用ILAsm(CIL Assembly Language)写的例程等
什么是CTS?
CLR是完全围绕类型展开的,通过类型,通过一种编程语言写的代码可以与另一种语言写的代码沟通。所以Microsoft制定了一个正式的规范,“通用类型系统”(Common Type System,CTS),它描述了类型的定义和行为。上面已经提到,Microsoft已经将CTS和,NET Framework的其他组件 -- 包括(文件格式、元数字、中间语言及对底层平台的)提交给ECMA完成标准化工作,最终形成的标准称为“公共语言基础结构”(Common Language Infrastructure)。
CTS里定义了以下内容:
什么是CLS?
CLR中集成了多种语言,一种语言中可以调用另一种语言创建的对象。为了实现这一目的,微软定义了一个公共语言规范(Common Language Specification)。它详细定义了一个最小的功能集。任何编译器如果想生成类型兼容于其他“符合CLS,面向CLR语言”生成的组件,那么必须要支持这个最小功能集。下面用一张图来说明彼此间的关系:
什么是IL?
先从上图看一下.net的编译过程,源代码文件经过编译器编译后生成托管模块(managed module)。托管模块说白了就是一个32位(PE32)或64位(PE32+)的PE文件。
具体的结构如下:
PE32或PE32+文件头 |
CLR头 |
元数据 |
IL代码 |
这里我们着重谈谈IL,IL是与CPU无关的机器语言,比大多数的CPU机器语言都要高级。能访问操作对象类型、创建及初始化对象、调用对象上的虚方法,直接操作数组元素,甚至能抛出和捕捉异常处理,可以说是一种面向对象的机器语言。
什么是JIT?它是如何工作的?
CLR执行托管模块,必须要将IL代码编译成本地CPU指令。这个事情由JIT(just in time)来执行。下面将以一个简单的例子来详细阐述JIT是如何工作的:
static void Main() { Console.WriteLine("Hello"); Console.WriteLine("Goodbye"); }
1、CLR在执行Main函数检查引用的所有类型,这里Main用到了Console类,CRL会为此分配一个内部结构。在这个结构中,Console类的所有方法都有对应的入口。
2、Mian首次调用WriteLine函数时,JITCCompiler函数会被调用。JITCCompiler知道是Console类定义了WriteLine函数,并在定义了Console类的程序集中查找WriteLine的IL代码。
3、动态分配一块内存;
4、对找到的IL代码进行验证,然后编译成本地CPU指令,将该指令放到前面动态分配的内存中;
5、修改内部结构中WriteLine函数的入口,使其指向动态分配的内存的地址;
6、JITCCompiler函数跳转到存有本地CPU指令;
7、执行完后,调回Main函数,继续执行后面的代码;
这里需要说明的是,第二次执行WriteLine函数的时候,并没有JIT啥事情了,因为会直接调用内存块中的本地CPU指令。
参考:《分清“语言/规范”以及“平台/实现”,以及跨平台.NET开发》
《CLR.via.C#》