How to Develop a Game
Mono and IL2CPP
PublishDate: 2025-06-01 | CreateDate: 2025-06-01 | LastModify: 2025-06-01 | Creator:ljf12825

MonoIL2CPP都是Unity的脚本运行后端(Scripting Backend),它们是两中IL语言的处理方式;Mono的处理方式类似C#程序的执行,而IL2CPP则是将其转化为C++代码,交由C++编译器处理

Mono

Mono是一个跨平台的.NET运行时实现,最初由Xamarin公司开发,用来在非Windows平台上运行C#程序;Unity在早期就选择了Mono作为C#脚本运行环境

核心组件:

特点:

局限性:

IL2CPP

IL2CPP是Unity自己研发的脚本后端,意为:

工作流程:

  1. C#脚本 -> 编译成IL(和Mono一样的中间语言)
  2. Unity的IL2CPP工具链把IL翻译成C++代码
  3. 使用C++编译器编译生成目标平台的机器码
  4. 最终得到一个完全原生的二进制可执行文件

特点:

局限性:

总体流程

  1. 从C#到IL(中间语言) 无论是Mono还是IL2CPP,第一步都是一样的:

这一步和普通.NET程序是一样的

  1. Mono路线(编辑器/Debug模式常用) Mono的工作流是:

特点:

  1. IL2CPP路线(发布版本常用) IL2CPP的流程比Mono多几步

特点

流程图


Mono vs IL2CPP

特性MonoIL2CPP
编译方式JIT / 解释执行AOT(IL → C++ → 原生机器码)
性能中等,依赖 JIT 优化高,接近 C++ 原生性能
平台支持跨平台,但某些平台有限制(如 iOS 禁止 JIT)全平台,尤其是移动端和主机端首选
反射完整支持部分受限,需要 link.xml 保留
编译时间快,开发迭代效率高慢,需要 C++ 编译整个工程
安全性容易被反编译(ILSpy 直接看源码)较难逆向(但不是绝对安全)

使用场景:
选择Mono的情况

选择IL2CPP的情况

Unity的选择

Unity的策略其实是


迁移注意事项: 从Mono迁移到IL2CPP时需要注意:

Unity Packaging and Building

在Unity中构建时,无法直接修改Mono或IL2CPP的底层C/C++代码,因为它们是与Unity引擎核心打包在一起的编译好的二进制库
但是,Mono和IL2CPP的选择并非一个简单的构建选项切换,
它是一项核心的架构决策,直接影响项目的性能特征、内存模型、平台兼容性以及后期优化策略
所以理解它们的行为差异,并以此指导上层C#代码编写、项目架构设计和性能优化策略很重要

内存管理

GC的深层机制对比

Mono

Mono使用的是BoehmGC

BoehmGC全称:Boehm-Demers-Weiser Conservative Garbage Collector,由Hans Boehm(HP实验室)编写,它是一种保守式垃圾回收器(Conservative Garbage Collector),它的主要作用是为C/C++/其他非托管语言提供垃圾回收支持;Mono在早期(Unity 4.x ~ 2018.1之前)选择直接集成现成的、稳定的BoehmGC作为默认垃圾回收器

BoehmGC的工作原理
和现代.NET CLR / Unity新GC不一样,BoehmGC是保守式GC,它具有以下特点

BoehmGC in Unity\

这就是为什么老Unity游戏经常出现掉帧/卡顿,原因之一就是BoehmGC的GC暂停

IL2CPP

IL2CPP不是一个运行时,它只是IL -> C++ -> 机器码的转换工具链
真正执行的时候,Unity还是需要一个托管内存的GC来帮忙管理C#对象,它会绑定到Unity内置的GC实现

IL2CPP编译出来的C++代码

现实意义

高级优化策略

编译与链接

泛型代码共享的深层原理

链接器(Linker),link.xml与代码裁剪(Code Stripping)

Linker

在Unity里,Linker是构建管线中的一个步骤:

Code Stripping

代码裁剪就是Linker的主要工作

原理:

优点:

缺点:

link.xml

为了解决“代码被误裁掉”的问题,Unity提供了link.xml配置文件,显式告诉Linker:哪些类/方法/程序集不要裁剪

<linker>
<!-- 保留整个程序集 -->
<assembly fullname="MyGameAssembly" preserve="all"/>

<!-- 保留特定类型 -->
<assembly fullname"UnityEngine">
  <type fullname="UnityEngine.GameObject" preserve="all"/>
</assembly>

<!-- 保留某个类的特定方法 -->
<assembly fullname="MyGameAssembly">
  <type fullname="MyNamespace.MyClass">
    <method name="MyMethod" />
  </type>
</assembly>
</linker>

preserve属性

Unity规定:

只要它在Assets目录下,Unity构建时就会自动收集并传递给Linker

多个link.xml的情况

构件时打开Editor log,会看到link.xml被解析的日志

实际使用场景

需要写link.xml的情况

  1. 反射:
  1. 序列化:
  1. 热更新框架
调试与问题定位

平台特定深度调优

  1. iOS

  2. Android

  3. WebGL


未来发展趋势

Unity正在持续改进IL2CPP,缩短构建时间、增强调试支持、改进泛型处理、更好的异常处理、增量构建支持等。随着Unity的发展,IL2CPP正成为更主流的选择,特别是在性能敏感和移动平台项目中

架构层面的考量