Mono运行时
1、两种编译器:
(1) C#编译器mcs(Mono2.11版本以上):将C#编译为ECMA CIL标准的byte code;
(2) Mono运行时中的编译器:将CIL的byte code转移为原生码。
2、三种转译方式:
(1) 即时编译(Just-in-time,JIT):程序运行过程中,将CIL的byte code转译为目标平台的原生码。
整体流程:
a.编译器将游戏脚本编译为托管模块(PE文件),除了编译生成CIL码外,编译器还会在托管模块中生成metadata元数据,元数据主要用来描述该模块定义了什么。
b.编译器默认将生成的托管模块转换为程序集(Assembly,通常是一些以Assembly开头的dll文件),程序集是由一个或多个模块\资源文件的逻辑性分组。程序集有一个数据块用来存放它下面所有托管模块的元数据表,即manifest清单,其内容主要包括:构成程序集的文件、程序集中文件所实现的公开导出的类型,以及和程序集相关联的资源或数据文件。程序集是重用、安全性以及版本控制的最小单元。
c.使用JIT编译执行程序集代码。首先将Mono运行时加载进入内存,目标系统创建进程,然后进程的主线程调用方法初始化Mono运行时并加载所需的程序集,之后调用入口方法。Mono运行时分配内部数据结构,在这个结构中,类型定义的方法都有一个记录项,每个记录项都有一个对应的地址,通过地址可找到方法的实现,该数据结构进行初始化时,Mono运行时会将每一个记录项都设置成包含在Mono运行时内部的一个叫作JITCompiler的未编档函数,之后Mono运行时通过调用JITCompiler函数进行JIT编译过程。
JIT运行新代码就是将生成的机器码映射到内存中,然后执行。由于IOS封存了内存(或者堆)的可执行权限,相当于变相的封锁了JIT这种编译方式。
(2) 提前编译(Ahead-of-time,AOT):程序运行之前,将.exe或.dll文件中的CIL的byte code部分转译为目标平台的原生码并且存储,程序运行中仍有部分CIL的byte code需要JIT编译。
(3) 完全静态编译(Full-ahead-of-time,Full-AOT):程序运行前,将所有源码编译成目标平台的原生码。
Full-AOT的限制:a.不支持泛型虚方法;b.不支持对泛型类的P\Invoke;c.不能使用反射中的Property.SetInfo给非空类型赋值;d.值类型作为Dictionary的Key时会有问题,实际上实现了IEquatable<T>的类型都会有问题;e.由于不允许动态生成代码,因此无法使用System.Reflection.Emit,无法动态创建类型,无法使用DLR及基于DLR的任何语言。
3、垃圾回收器(GC,管理的是Mono的托管堆,引用类型会被分配在托管堆上)
(1)贝姆垃圾收集器(Boehm):基于Mark\Sweep,无分代\并行;执行时所有线程阻塞;每次标记都会扫描访问到所有可到达的对象(穷举搜索垃圾)。所有这种方式极有可能以一定时间间隔造成帧率下降,影响玩家体验;
(2)分代收集(genetational collector,Mono2.8版本后才有)
GC触发:(1)堆内存不足,自动调用GC;(2)手动调用