跳到主要内容

GC 差异 — 理论分析对比

性质: 理论分析,非 benchmark。
ZLua 细节:spec/10-LIFETIME.mdspec/marshal/ 为准。


1. 分析轴

问题
热路径分配每次 Lua↔C# 是否必然 object[]、装箱、新 string
userdata 与托管对象一对一?弱表?registry 槽?
struct拷贝 / 池化 / Opaque 临时句柄
字符串Push/Pop 是否每次新托管 string
Delegate / 闭包桥接对象生命周期与 ref
峰值 vs 稳态首次 Bind / 首次 delegate vs 热循环

2. 四方案热路径分配画像(典型)

2.1 xLua

路径典型分配
Lua→C# 简单方法生成 Wrap 通常无 object[];参数为值类型时较干净
Lua→C# 重载 / 反射兜底可能 object[]params 装箱
C#→LuaLuaFunction.Call 等可能分配;多次 LuaDLL
userdata ↔ objectObjectTranslator 池 + 弱引用;Push 新对象可能分配 wrapper 信息
stringLua string ↔ UTF-16 通常分配string
struct装箱 或 table 中转(视 Wrap)
DelegateDelegateBridge + translator 条目;首次绑定分配

「零 GC」: 官方与社区均 承诺热路径零 GC;blittable 热循环可接近零分配,string/object 必分配。

2.2 toLua / tolua#

路径典型分配
Wrap 调用类似 xLua;早期版本热路径相对粗糙
出参 / ref常 table 或多返回值,可能临时 table
string / object与 xLua 同量级
struct多依赖导出策略,装箱常见

「零 GC」: 成立为通用承诺。

2.3 SLua

路径典型分配
自动绑定与 toLua 类似
值类型优化部分版本有 struct 优化,仍依赖导出
DelegateLuaFunction 转换常分配

「零 GC」: 通用;需 per-API profiling。

2.4 ZLua(Il2Cpp Player 设计目标)

路径典型分配
Lua→C# blittable 方法C++ 桥直接读栈 → methodPointer目标零 GC
Lua→C# string / classstring 分配;class ObjectRegistry Push(槽位 + 弱缓存)
C#→Lua [LuaInvoke] blittableC++ PushDefault*;目标零 GC
C#→Lua ref/outOpaqueValue(lightuserdata,无托管 boxing)
struct ByValuserdata 内 payload 拷贝;non-blittable 可能 boxed companion
Delegateclosed delegate + funcRef;首次绑定有分配,稳态 待测
Mono Editor Emit设计为 热路径 object[] + Method.Invoke;与 Player 语义一致

3. ZLua 核心机制

3.1 ObjectRegistry(ByObj)

管理 class / string / array / delegate / boxed enum 等 ByObj userdata:

Push → 分配 slotIndex → _registeredObjects[] + GC root(Il2Cpp)
→ 弱值缓存 (obj, viewKlass) → 避免重复 Push
__gc → UnregisterObject → 解除 root
GC 含义
slot + rootLua 持有 userdata 期间,阻止 Il2Cpp 回收该对象
弱缓存命中时不新 Push;未命中一次 Push 成本
Pop不分配;仅查 slot

详见 spec/10-LIFETIME.md §2。

3.2 ByVal struct

形态分配
ByVal userdatapayload 在 userdata 内 拷贝__gc 释放 native 拷贝
non-blittable ByVal可能 boxed companion + NotBlittableStructRegistry 扫描
zlua.box → ByObjboxing 为 ByObj,走 ObjectRegistry

Lua→C# 传 struct: 默认 ByVal 拷贝; 每次装箱(除非 ByObj 路径)。

3.3 OpaqueValue(C#→Lua)

说明
形态lightuserdata handle,指向 C# 侧 ref 槽
生命周期仅当次 C#→Lua 调用帧;跨 pcall 保存 → error
GChandle 本身 增加托管对象计数;指向的 ref 槽在 invoke 内有效

用于 [LuaInvoke] / delegate bridge 的 ref/out/in 默认路径(见 spec/marshal/04-OPAQUE.md)。

3.4 Indexer 与分配

模式分配影响
Il2Cpp Dispatch* indexerC++ 路径; 因 indexer 额外分配
Mono 三表 Lua indexer纯 Lua 表查找;miss 返回 nil, 临时对象

4. 「零 GC」声称的边界条件

以下「零 GC」指 稳态热循环、GC Alloc ≈ 0(Unity Profiler / dotMemory 意义下), 绝对无 native 侧 malloc。

4.1 可能接近零 GC 的 ZLua 路径(Il2Cpp Player)

条件示例
签名全 blittablevoid Tick(float)int Add(int,int)
Lua→C# 无 new string / class仅 number + 已存在 userdata
C#→Lua 返回值 blittableintfloat、无 string
已 Bind,无首次 EnsureBinding热循环内
无 ref/out 跨帧 OpaqueOpaque 仅在单次 invoke 内使用

4.2 必然或极可能有分配的路径

路径四方案
new string 跨边界均可能分配 UTF-16 string
new class 从 C# 返回 LuaZLua ObjectRegistry Push;xLua translator
装箱 enum / struct(若走 ByObj)均可能
首次 类型 Bind / delegate 绑定均可能
Lua table ↔ C# collection[LuaMarshalAs] / API,常分配
重载 dispatch 失败重试可能临时对象(待测

4.3 四方案「零 GC」对照(诚实)

声称成立范围
xLua 热路径可优化需 Generate + 避免反射; 全局零 GC
toLua / SLua一般不 宣传零 GC
ZLua Il2Cpp blittable 热路径 目标 零托管 GC;string/object/delegate 首次绑定

5. 峰值 vs 稳态

阶段xLuatoLua / SLuaZLua
首次访问类型Generate 已做则 Wrap 就绪导出已生成EnsureBinding + stub 注册(Il2Cpp)
首次 Push 某 objecttranslator 登记池登记ObjectRegistry slot + 缓存
热循环 100 万次 P1分配应 ≈0(Wrap 良好时)类似目标 ≈0
每帧 1000 次 string API分配与 GC 压力主导同左同左

6. Delegate 与 Lua ref 生命周期

方案模型
xLuaDelegateBridge 持有 ref;需在适当时机 Dispose / GC
toLua / SLuaLuaFunction 生命周期手动管理
ZLuafuncRef + closed delegate;ProcessPendingRefReleases 帧泵延迟释放(spec/10-LIFETIME.md

迁移注意: 不要以为「C# delegate 已不被引用」Lua 侧 function 会立刻释放;须理解 ZLua ref 释放语义。


7. Il2Cpp GC 集成(ZLua 特有)

机制目的
ObjectRegistry 槽位 GC rootLua userdata 存活 → 托管对象不被 Il2Cpp 单独回收
non-blittable struct push_other_roots扫描 struct 内存内引用字段
userdata __gc与 Lua GC 同步 Unregister

xLua / toLua / SLua 一般不改 Boehm/Il2Cpp GC root 策略;ZLua 为正确性 刻意 集成。


8. 分析建议(工程实践)

  1. 先定场景: P1 式热循环 vs P6 式 string 密集。
  2. Profiler 看 Alloc: 若每 call 有 System.String / Box / object[],互调优化收益有限。
  3. 双端跑: Mono 与 Player GC 行为可能不同;以 Player 为准
  4. 读 spec: struct/ref 路径见 spec/marshal/03-BYREF.md05-STRUCT.md

相关文档

文档内容
PERFORMANCE.md性能场景 P1–P6
impl/marshal/REGISTRIES.mdRegistry 实现
FEATURES.md值类型用法差异

理论分析稿;四方案同场景 GC Alloc 对比 待测