ILRuntime¶
它解决什么问题¶
Unity 在 iOS、主机等平台通常使用 IL2CPP,把 C# IL 提前编译为 C++ 与机器码。应用发布后,原生代码不能像脚本一样任意替换,因此需要一种在运行时加载并执行热更程序集的机制。
ILRuntime 用纯 C# 实现了一个 IL 解释器与运行时环境,使热更 DLL 能在不依赖 JIT 的平台上执行。它维护自己的类型系统、堆栈帧和调用约定,并通过绑定层与 Unity 主工程交互。1
两个域¶
| 域 | 内容 | 特点 |
|---|---|---|
| 主工程 AOT 域 | Unity 引擎、基础库、稳定框架 | 编译后不可替换 |
| 热更域 | 业务逻辑、活动、UI 流程 | 由 ILRuntime 解释执行 |
跨域调用是性能与复杂度的核心。每一次热更代码访问 AOT 类型,都需要经过绑定与适配。高频路径如果不做绑定优化,开销会非常明显。
常用优化手段¶
CLR 绑定¶
为常用 AOT 类型生成绑定代码,减少反射与动态查找。适合:
Transform、GameObject等高频 Unity API;- 热更层频繁调用的框架接口;
- 值类型与泛型方法;
- 事件、委托与跨域继承。
寄存器模式¶
ILRuntime 2.x 引入寄存器式执行模式,可减少部分数学与局部变量操作的解释开销。实际收益取决于代码形态,仍需以 Profiler 为准。
减少跨域热点¶
- 不要把逐帧逻辑拆成大量细碎跨域调用;
- 批量传递数据,而不是每项调用一次;
- 在 AOT 侧提供更粗粒度的接口;
- 避免在
Update中频繁创建热更委托; - 对热更层做独立性能采样,而不是只看总帧率。
泛型与 AOT 元数据¶
泛型是热更新方案最容易出问题的区域之一。AOT 编译器会为值类型泛型生成具体实现,但热更程序集在运行时才出现,可能找不到对应实例化。
常见处理:
- 在 AOT 侧显式引用热更代码会用到的泛型组合;
- 使用
link.xml保护被裁剪的类型与成员; - 为常用泛型接口生成适配器;
- 在构建后扫描热更程序集,检查缺失的泛型实例化。
编辑器能跑不代表真机能跑
编辑器可能使用 Mono JIT,路径与 IL2CPP 完全不同。热更代码必须在目标平台的 Development Build 上验证程序集加载、泛型、反射与裁剪。
跨域继承与适配器¶
热更层继承 AOT 类型或实现 AOT 接口时,需要生成适配器,把调用转发到热更实现。适配器是功能边界,也是性能边界;应避免在每帧路径中大量创建适配器对象。
发布流程¶
- 主工程与热更程序集使用兼容的 API 基线;
- 热更 DLL 进入内容包,并携带程序集哈希与版本号;
- 启动时校验版本、依赖与 AOT 元数据;
- 加载失败时回退到内置稳定程序集;
- 线上记录加载耗时、解释器异常与跨域调用错误;
- 保留上一版本内容,支持快速回滚。
取舍¶
ILRuntime 的优势是纯 C#、方案完整、工具链成熟;代价是解释执行性能、跨域绑定复杂度以及泛型与裁剪问题。对于逻辑复杂、热更频率高但性能敏感路径较集中的项目,它仍然是一个可维护的选项。
完整的加载顺序、裁剪、序列化和回滚问题,见热更新常见问题与排查。