最近在研究学习C#的时候,研究了一下C#的运行原理和编译过程,多次查找各种文章和书籍总结后得出的结论,新手入门,只为总结记录,有问题的地方还望有人能看见指正
C#的特点
首先先说一下C#的一些特点:
第一,C#是一种高级程式语言,所谓高级程式语言,和低级程式语言相比,更偏向于人的理解,所以需要进行编译转化为低级程式语言,才能够被计算机所能理解,因为计算机只认cpu指令,而人对cpu指令理解起来有难度。
高级程式语言有:C,C++,Java,C#,python等
低级程式语言:机器语言,嵌入式,汇编语言等
第二,从类型的角度来划分的话,有强弱两种类型,强类型语言就是需要强制数据类型定义,不允许隐式转换的语言,而弱类型语言就是可以忽略数据类型,允许隐式转换的语言,所以,C#自然是属于强类型语言,所以C#是一种相对安全的语言。
强类型语言有:Java,C#,python等
弱类型语言有:C,C++,JavaScript等
第三,从确定类型的时间划分的话,有动静两种类型,动态语言就是在运行的时候才检查确定数据类型,而静态语言就是在编译的时候就检查确定数据类型,所以,C#属于静态语言。
动态语言有:python,PHP,JavaScript等
静态语言有:C,C++,C#等
C#的两次编译
C#编译的过程中会进行两次编译,第一次是使用的IDE,比如VScode自带的编译器进行编译,第二次就是JIT编译。关于第一次编译没有过多的深入研究,JIT编译会在后面进行详细研究。
编译过程
C#的编译过程是从源代码开始的,通过IDE自带的编译器对源代码进行编译和检查,会生成托管模块,托管模块是可以多个或一个合成程序集,也就是我们常用的.dll和.exe文件,而程序集是不能自己执行的,所以必须要通过搭载在一个平台上进行运行, 这里就会用到CLR,CLR是一种虚拟机,它的其中一种功能就是加载程序集,然后在CLR上,会进行JIT编译,将IL编译成CPU指令。
过程中有几个重要的东西会在下面详细解释。
1.托管模块
2.CLR
3.JIT编译
托管模块
首先,托管模块一般是由四部分组成的,分别是CLR头,IL头,元数据和IL,其中,CLR头和IL头都是描述文件,本文不做过多解释,这里将一下元数据和IL,因为这两个涉及到后面的JIT编译,而且很重要。
元数据中一共有三张表去记录和描述程序中使用到的文件的信息,分别是:
1.引用表:也就是在引用到的类型和成员
2.定义表:定义的类型和成员
3.清单表:程序集中使用到的文件的信息
IL:中间语言,它是将源代码转换为机器语言中间的一种伪机器语言,作用就是能够通过IL,实现程序跨平台和灵活性等优点。
CLR
公共语言运行时,又叫公共语言运行库,是和Java虚拟机一样的一种运行时环境,CLR的功能包括:内存管理,程序集加载,安全性,线程同步和异常处理,这些都可以由面向CLR的所有语言使用,并保证底层应用和操作系统必要的隔离。
因为C#本身和Java一样,并不支持跨平台,但是可以通过CLR实现跨平台,各个面向CLR的语言都可以通过进行第一次编译成IL,然后通过CLR加载,再进行JIT编译成CPU指令,从而实现跨平台。
JIT编译
上面讲过在程序集通过CLR加载后,会进行第二次编译,也就是JIT编译,JIT编译就是即时编译,下面记录一下JIT编译的具体过程。
首先在Main函数执行前,会现在内存中开辟一块空间,来存储获取到的Main函数中的所有数据类型,这些数据类型的方法都会有一个入口,每一个入口都对应一个地址,这里为了分辨我们叫它地址A(通过这个地址A能够找到引用的方法),而这些入口在编译的时候就会变成JITCompiler函数。
在进入到JITCompiler函数体中以后,会通过元数据中的三个表记录的信息匹配到使用到的类名和函数名,这里能够通过元数据记录的信息得到IL中的地址,通过编译地址对应的IL,得到Cpu指令,存储到地址B,最后将地址A替换为地址B。
通过JIT编译不仅能够优化热点代码,还能提高效率,在实现跨平台的时候,不需要每一次执行前都全部编译,可以直接使用地址B的指令。
粗略的画了一个JIT编译过程图,希望能够帮助理解,有错望指正。
跨平台
接下来,记录一下对跨平台的粗略理解。
首先,C#本身是一种不支持跨平台的语言,所以需要一个虚拟机才能实现跨平台,而在Windows系统下,这个虚拟机就是CLR,而在比如Unity3D或Linux或其他平台,想要实现跨平台就需要使用Mono,Mono同样也是一种虚拟机。
正因为Unity3D中内嵌了一个Mono,所以说Unity3D可以通过Mono实现跨平台,这也是为什么能够使用多种语言对Unity3D的脚本进行编写。
实现跨平台可以理解为不同的语言编译成IL在不同的平台上通过不同的虚拟机的运行来实现跨平台。
只为记录笔记,有错误希望大佬能够指正。