跳到主要内容

少 GC Marshal

本篇只谈 少分配 / 零额外托管分配 的常用 Marshal 手段。默认规则见 Marshal 总览;特性语法见 TsMarshalAs。权威:04-OPAQUE02-MARSHAL-AS05-STRUCT

「少 GC / 0GC」指热路径上 尽量不 为这次互调新建 JS plain object / JS string / ByVal exotic / 装箱对象。并非整个程序永不 GC。

1. OpaqueValue:引用类型与 struct

OpaqueValue 是临时句柄(无成员分派),Push 时不建完整 ByObj/ByVal exotic。

场景行为
ref / out / in T(任意 T)C#→JS 默认 Opaque,无需标注
by-val 任意 CLR 类型可显式 [TsMarshalAs(OpaqueValue)]仅 C#→JS
public void Touch(ref Transform t) { } // 默认 Opaque
public void Peek([TsMarshalAs(TsMarshalType.OpaqueValue)] MyClass obj) { }
public void PeekStruct([TsMarshalAs(TsMarshalType.OpaqueValue)] Vector3 v) { }
// 在同一次 C#→JS 同步调用链内
const v = zts.get_opaquevalue(slot);
zts.set_opaquevalue(slot, newValue);
// 需要 ByVal 门面时:zts.to_user_data(slot)(会产生 ByVal exotic,见规范)

要点:

  • 引用类型struct 都可走 Opaque,避免为本帧临时对象建完整 exotic
  • 不可把 Opaque 存进跨帧 / 跨异步的长期表
  • JS→C# 单独形参上标 OpaqueValue 非法;写回规则见 ref/out/in
  • 失效后 → throw Error('zts: invalid opaque parameter handle')

2. ByVal:struct 默认与真 ref

路径说明
默认 by-val长生命周期常用 ByVal exoticnew Type(...)
ref/out/in + 同型 ByVal真 ref,可写回 payload,无需 Opaque API
zts.to_user_dataOpaque → 拷贝 ByVal(与原 handle 独立)

热路径若只需读字段一次,优先 Opaque;需要成员分派或长期持有再 ByVal。

3. UnpackedValues:struct 多槽展开

普通 struct / 闭合泛型 struct,用多连续实参 ↔ Members创建 plain object,也 创建 ByVal exotic:

public void ApplyForce(
[TsMarshalAs(TsMarshalType.UnpackedValues, Members = new[] { "x", "y", "z" })]
Vector3 force) { }
rb.ApplyForce(0, 9.8, 0); // 三槽;JS 侧只有 number
适用不适用
struct / closed generic structNullable<T>(规范禁止;无法用多槽表达「无值」)
热路径 Vector2 / Vector3 / 自定义 blittable 小结构class / interface

Nullable<struct> 需要「可空」语义时用 Tablenull↔无值),但 object 本身有 JS 分配,不算本节意义下的 0GC;C#→JS 临时可空值也可考虑 Opaque

4. Object:巨大 string 走 ByObj

默认:string ↔ JS string(按内容拷贝)。对超大文本,拷贝成本高:

public void ProcessHuge(
[TsMarshalAs(TsMarshalType.Object)] string payload) { }

标注后强制 ByObj exotic(托管 System.String),不再生成对应内容的 JS string。

注意

ByObj exotic 仍会在 JS 侧分配 exotic,参与 GC。只是避免「再复制一整份 JS string」。日常短字符串继续用默认即可。

byte[] 若要 octet 语义,用 Bytes(↔ JS string),与本节目标不同。

对照速记

手段典型目标JS 侧额外分配
OpaqueValueC#→JS 的 class / struct / byref无完整 exotic / plain object
ByVal exotic可长期持有的 struct、真 ref 写回有 ByVal exotic
UnpackedValuesJS↔C# 的 struct 字段展开无(只用栈上 number 等)
Tablestruct / Nullable<struct> 键值 plain object
Object on string巨大 string 避免 JS string 拷贝 ByObj exotic

引用类型默认走 Registry(ByObj);struct 默认 ByVal / 同步链 Opaque;热点再叠加上述策略。定性说明见 Marshal 概念

相关文档