直接跳到内容

WASM 垃圾回收提案

提案背景与意义

WebAssembly (Wasm) 最初设计为低级的二进制指令格式,主要面向数值计算密集型任务,缺乏对高级语言垃圾回收 (GC) 的原生支持。这意味着 Java、C#、Kotlin、Dart、Scala 或 OCaml 等具有垃圾回收机制的语言在编译到 Wasm 时,必须携带自己的 GC 系统,导致模块体积膨胀性能开销增加

WasmGC 提案通过引入原生垃圾回收机制,旨在为这些托管语言提供更自然的编译目标。它使 Wasm 能够更高效地支持高级语言,减少手动内存管理的负担,同时避免通过模拟 GC 带来的性能开销。

核心机制与类型系统

类型系统革新

WebAssembly 3.0 引入了一套全新的垃圾回收类型系统,建立在两种基本关系之上:

  1. 子类型关系:使用 (sub [final] [type-id] (...)) 语法定义,类似于面向对象编程中的基类-派生类关系。
  2. 类型等价关系:处理递归类型定义时的复杂等价性判定。

类型系统示意图:

类型关系
├── 子类型关系 (sub <final> <type-id> ...)
└── 类型等价关系
    ├── 递归类型块 (rec ...)
    ├── 内部引用 → (rec.i) 相对引用
    └── 外部引用 → 绝对引用

结构化数据类型

WasmGC 通过扩展 Wasm 的类型系统和指令集,支持结构体 (struct)数组 (array) 等高级数据类型。这使得编译器可以直接定义数据布局,而由 Wasm 引擎负责内存的分配和回收。

结构化数据示例:

wasm
(module
  (type $Person (struct
    (field $name (ref string))
    (field $age i32)
  ))

  (func $createPerson (param $name (ref string)) (param $age i32) (result (ref $Person))
    (struct.new $Person
      (local.get $name)
      (local.get $age)
    )
  )

  (func $getAge (param $person (ref $Person)) (result i32)
    (struct.get $Person $age (local.get $person))
  )
)

代码来源:

递归类型与等价性处理

递归类型块 (rec ...) 是类型系统的重要特性。在类型检查过程中:

  • 内部引用会被替换为 (rec.i) 形式的相对引用。
  • 外部引用则保持为绝对引用。
  • 类型等价性的判定规则要求:两个类型在各自递归块中的相对引用相同,且所在递归块相等时,它们才等价。

垃圾回收工作原理

内存管理模型

WasmGC 引入了自动内存管理机制,通过垃圾回收算法自动回收无用内存。与传统的线性内存模型不同,GC 内存由 Wasm 运行时自动管理,无需开发者手动分配和释放。

内存划分示意图:

WasmGC 内存空间
├── 栈内存 (存储局部变量和函数调用上下文)
└── 堆内存 (动态分配)
    ├── 存活对象 (被根对象或其它对象引用)
    └── 垃圾对象 (无法被访问,等待回收)

标记-清除算法

WasmGC 采用标记-清除算法进行垃圾回收。该过程分为两个阶段:

  1. 标记阶段:遍历所有从根对象 (如全局变量、当前函数调用栈中的局部变量等) 出发可达的对象,并标记它们为存活。
  2. 清除阶段:回收所有未被标记的对象占用的内存,并将其返回给空闲内存池。

对象回收示意图:

标记前: [对象A]→[对象B]→[对象C]→[对象D]   (根引用A和C)
标记后: [对象A*]→[对象B*]  [对象C*]→[对象D]  (*表示存活)
清除后: [对象A]→[对象B]  [对象C]          (D被回收)

类型化引用与安全调用

作为 GC 的基础,Wasm 的类型系统得到了显著增强。类型化引用可以精确描述其所指向的堆值的结构,从而避免了运行时的安全检查。通过新的 call_ref 指令,可以实现完全类型安全且无开销的间接函数调用

性能优化策略

静态类型优势

WasmGC 的设计注重性能,通过静态类型信息在编译时已知对象布局,避免运行时类型查询,从而减少 GC 开销。

规范化技术

为了提高性能,WebAssembly 实现通常采用“规范化”技术:

  • 将所有内部引用替换为 (rec.i) 形式。
  • 将所有外部引用替换为其指向的规范化类型。
  • 通过哈希一致化技术优化存储。
  • 在规范化后的类型上,等价性检查简化为指针比较。

确定性执行支持

为了满足区块链、可重现系统等场景对确定性执行的需求,Wasm 标准定义了确定性配置文件。它为浮点数运算和松弛向量指令规定了统一的默认行为,确保了在遵循该配置文件的平台之间,Wasm 程序是完全可移植且可复现的。

与宿主环境集成

JavaScript 交互优化

针对 JavaScript 环境,Wasm 3.0 新增了 JS 字符串内建函数。Wasm 模块可以导入一组新的内建函数,用于直接在 Wasm 代码内部操作作为 externref 传入的 JavaScript 字符串,提升了与宿主环境的交互效率。

跨语言内存管理

WasmGC 允许与 JavaScript 的 GC 协作,避免跨语言内存泄漏。通过定义清晰的接口和引用规则,确保 Wasm 模块与宿主环境之间的对象引用得到正确管理。

跨语言引用示意图:

JavaScript 环境
    ↓ 引用
WasmGC 对象 ←→ 宿主环境 GC
    ↓ 协作
统一的内存管理

工具链与生态支持

编译器工具链进展

WebAssembly 生态系统正在积极适应 GC 提案。Wasm-tools 项目在 1.233.0 版本增强了对组件模型中更多类型的 GC 支持,为变体、选项和结果等高级类型添加了 GC 降低支持。

同时,新的 MLIR-based Wasm 编译器管道解决了现有编译器对高级功能支持不足的问题。通过引入 MLIR Wasm 方言,支持更高效地编译具有高级语言特性的代码。

多语言支持

WasmGC 使多种编程语言能够更有效地编译到 WebAssembly:

  • Java/Kotlin:无需携带完整的 JVM 运行时。
  • OCaml:利用 WasmGC 的类型系统表达 OCaml 的 ADT。
  • Dart:减少生成的 Wasm 模块大小。
  • Python:通过 Pyodide 项目在浏览器中高效运行。

应用场景与未来展望

实际应用场景

WasmGC 为多种应用场景带来了改进:

  1. 影音处理与游戏:通过 GC、例外处理与 SIMD 的改善获得更佳体验。
  2. 科学计算:处理更大数据集,得益于 64 位地址空间和多内存支持。
  3. 前端编辑器:更快的启动速度和更小的模块体积。
  4. 服务器端应用:在非 Web 环境中处理海量数据。

技术展望

随着 WebAssembly GC 在 2022 年 12 月达到 Stage 4 阶段,该技术已趋于成熟并具备了广泛应用的基础。未来,随着更多工具和优化技术的出现,开发者将能够更高效地管理 Wasm 的内存和进行垃圾回收。

WASM 垃圾回收提案已经加载完毕