Unity中的C#底层原理

一、Unity如何运行C#

1.1 Mono和IL2CPP

了解Mono和IL2CPP之前我们先了解一下.NET FrameWork和IL(CIL),.NET FrameWork是Windows的托管环境,可为其运行的应用提供各种服务,主要包括两个组件:公共语言运行时(CLR),它是处理运行应用的执行引擎;NET FrameWork类库:它提供开发人员可从其自己的应用中调试的已测试、可重用代码库.

  • Mono:基于Unity的一个开源、跨平台的.NET框架实现,C#脚本先被Mono的C#的编辑器转换为中间语言IL(公共中间语言CIL),然后在Mono虚拟机上运行,这种方式支持跨平台运行,但是需要在目标平台上安装或移植Mono虚拟机

  • IL2CPP:IL代码被转换为C++源代码,然后编译成目标平台的本地代码,这种方式能够提高了运行效率,并且可以在不支持JIT的编译器的平台上运行,如IOS和某些游戏主机平台.

  • Mono运行流程图:

    Mono运行流程图

  • IL2CPP流程图:

1.2 编译模式

  • Just-in-time(JIT):即时编译模式,Unity在程序运行时将CIL语言通过Mono虚拟机转译成机器码,然后执行(Mono执行方式).
  • Ahead-of-time(AOT):提前编译模式,Unity在程序打包时将IL转换为C++源代码,在编译成目标机器的本地代码并存储在文件中,但程序运行中仍有部分的CIL需要JIT编译.
  • Full-ahead-of-time(Full-AOT):完全静态编译,程序运行前将所有的源码编译成目标平台的机器码(IL2CPP的执行方式).

1.3 为何引进IL2CPP

​ 早期,C#是微软的,只能在Windows平台上运行,为解决跨平台的问题,引入了Mono,利用Mono的虚拟机运行在各个平台上,而IL2CPP是因为Mono的平台移植有限,运行效率过低等缺陷,从而加入了IL2CPP的机制.

  • 可移植性强:Mono在各平台完成移植的工作量大,每新增一个平台就要把虚拟机移植一遍.ios平台不支持Mono虚拟机的运行,而IL2CPP可以通过c++直接在对应平台上运行.
  • 运行效率高:由于IL2CPP直接打包为C++代码,可以直接由各平台的C++编辑器转化为机器码,省去了在运行时解释执行的步骤,所以运行效率相比Mono提升了40%左右.

二、Unity的垃圾回收机制

2.1 托管代码和非托管代码

  • 托管代码:托管代码是在运行时环境中执行的代码,虚拟机的JIT编译执行的IL代码,其中对象无须手动释放,而是由GC管理(C#、VB.NET、F#).
  • 非托管代码:C/C++或C#中以不安全类型写的代码.虚拟机无法跟踪到这类代码的对象,其直接操作系统资源和内存,需要程序员手动管理内存分配和释放.

我们一般使用托管代码来编写游戏逻辑,而非托管代码通常用于更底层的架构、第三方库或操作系统接口.

2.2 GC垃圾回收

GC的全称是"Garbage Collection",也就是垃圾回收,他是一种自动管理堆内存的机制,用于管理对象的分配和释放.只对托管代码进行管理.

Unity基于Mono和IL2CPP的GC机制都是标记/清除(Mark-Sweep)算法,这个算法遍历内存中的所有对象,找出所有对象的引用关系,把这些正在使用的引用对象标记出来,然后清除没有标记的对象.但是这个算法会存在内存碎片化的问题.

  • IL2CPP:C#本身就有GC,但是C++没有,所以在需要IL2CPP的runtime库实现这个GC算法.