Unity3D中C#底层原理

转载:部分来源zhihu
摘抄:Unity3D高级编程主程手记 陆泽西 著

Unity底层在运行C#程序有两种机制:一种是Mono,另一种是IL2CPP。
.NET虽好,却只能运行在Windows平台上(现在NetCore可以跨平台,但是不完善)。后来微软想ECMA申请将C#作为一种标准,就意味着只要他遵守CLI的第三方就可以将任何一种语言是实现到.Net平台上。
Mono就是在这种情况下诞生的。

CIL:特指在.NET平下的IL标准。
IL:中间语言。是一种低阶的人类可读的编程语言。可以将通用语言翻译成IL,然后汇编成字节码,最后运行在虚拟机上;也可以把IL看作是一个面向对象的汇编语言,只是它必须运行在虚拟机上,而且是完全基于堆栈的语言。基于CLI规范的高级语言,会被各自的编译器编译成中间语言 IL(CIL),当需要运行他们的时候就会被实时的加载到运行时库中,有虚拟机动态的编译成汇编代码(JIT)并执行。
注:大部分情况下,IL和CIL都是表示同一个东西,即中间语言。
在Unity中编译器将编译成遵循CLI规范的IL以后,再有Mono虚拟机解释并执行。
编译的过程:
1、C#编译器:从Mono2.11版本开始,采用的编译器叫mcs,它的作用是将C#编译为CIL(Common Language Infrastructure,通用中间语言,也叫MSIL微软中间语言,这个语言能运行在所有支持CIL的环境中)
2、Mono运行时:运行时想要的是定义一个与具体语言无关的,跨架构系统的运行环境。其中有:即时编译器(Just-in-time,JIT)、提前编译器(Ahead-of-time compiler,AOT)、类加载器、垃圾回收器和线程系统等。C#代码经mcs编译成CIL后的byte code后就是在Mono运行时中经JIT或AOT转译为原生码(Native Code),这个原生码可以在当前机器运行,这就是Mono跨平台的原因。
3、基础类库:Mono平台提供了很多基础类,这些类库与.NET框架兼容。
4、Mono类库:Mono自带的类库。
总之,在我们使用Unity3D开发游戏的过程中,C#代码编译的过程为:
第一步:C#代码到CIL的编译(在mcs上发生)。
第二步:CIL到CIL位元码的编译(在mcs上发生)。
第三步:CIL位元码到本地指令的编译(在Mono运行时上发生,有3种转译方式,3种转译方式如下)
a. 即时编译(Just-in-time,JIT):在程序运行的时候把CIL的位元码转译为目标平台的原生码(机器码)。
b. 提前编译(Ahead-of-time,AOT):在程序运行前就把CIL的位元码转译为目标平台的原生码并且存储,但是并不是全部的CIL的位元码都会提前转译并存储,仍然会有一小部分的CIL位元码会在程序运行的过程中转译。简单来说就是AOT和JIT都有。
c. 完全静态编译(Full-ahead-of-time,Full-AOT):这种模式是完全的AOT,不允许在程序运行的过程中使用JIT编译。程序在运行之前所有的代码必须都已经被编译成了目标平台的原生码。在ios平台的Unity3D开发必须用到这种模式。
Mono内包含单大类的组件,分别是核心组件,Mono/Linux/GNOME开发堆栈,微软兼容堆栈。
1)核心组件:C#编译器,CLI虚拟机,核心类别程序库;
2)Mono/Linux/GNOME开发堆栈:提供了工具用于开发应用软件。
3)微软兼容堆栈:提供了一种方式以及使用Windows .NET应用程序可以一种到GNU/Linux上,堆栈包括了ADO.NET,ASP.NET,以及windows Forms等;
Mono使用了垃圾回收机制用来管理内存,应用程序向垃圾回收机制申请内存,最终又垃圾回收器决定是否回收。在3.1.1版本以后,Mono正式使用Simple Generational GC(SGEN-GC)设置为默认的垃圾回收器。主要将对象分为两个内存池,一个较新,一个较老的。存活时间长的就转移到老的内存池中去。
托管代码以及非托管代码:IL编码中,C#生成的IL编码成为托管代码,由虚拟机的JIT编译执行,其中的对象无需手动释放,他们有GC管理。C#中以不安全类型写的代码我们成为非托管代码,虚拟机无法跟踪这类代码对象。托管代码来编写游戏逻辑,非托管代码通常用于更底层的架构,第三方库,或者操作系统相关接口。
IL2CPP:
Unity使用IL2CPP的原因,官方给出的解释如下:
1)维护成本问题;
2)Mono版本授权受限;
3)提高运行效率;(提高1.5~2.0倍)
IL2CPP的编译和运行过程:
Mono将C#翻译成IL,IL2CPP在得到的中间语言IL以后,将他们重新变成C++代码,再有各个平台的C++编译器编译成能执行的机器码。
注意:
虽然C#代码被翻译成了C++,但是IL2CPP也有自己的虚拟机,只不过IL2CPP的虚拟机并不执行JIT或者翻译任何代码,它主要用于内存管理,其内存管理仍然采用类似Mono的方式。程序员使用IL2CPP就不需要关心Mono与IL2CPP直接的内存差异。
Unity在IOS平台使用基于AOT的完全静态编译绕过了JIT,Mono能在这些不支持JIT的操作系统中使用。对于IL2CPP来说,就相当于静态编译了C#代码,只是编译成了C++代码,最后翻译成二进制机器码绕过了JIT。所以也可以说IL2CPP实现了另一种AOT完全静态编译。

你可能感兴趣的:(Unity,c#,unity)