发布时间:2025 年 1 月 29 日
WebAssembly 垃圾回收 (WasmGC)
编程语言分为两类:垃圾回收型编程语言和需要手动内存管理的编程语言。前者的示例包括 Kotlin、PHP 或 Java 等众多语言。后者的示例包括 C、C++ 或 Rust。一般而言,高级编程语言更有可能将垃圾回收作为一项标准功能。
简而言之,垃圾回收是指尝试回收由程序分配但不再引用的内存。这种内存称为垃圾。实现垃圾回收的方法有很多。最容易理解的方法之一是引用计数,其目标是统计内存中对象的引用数量。
这可能听起来很奇怪,但编程语言是用其他编程语言实现的。例如,PHP 运行时主要采用 C 语言实现。如果开发者想要将 PHP 等语言编译为 Wasm,通常需要编译所有部分,例如语言的解析器、库支持、垃圾回收和其他关键组件。
Wasm 在浏览器中运行,并在主机语言 JavaScript 的上下文中运行。在 Chrome 中,JavaScript 和 Wasm 在 Google 的开源 JavaScript 引擎 V8 中运行。V8 已经有垃圾回收器。这意味着,如果开发者使用的是编译为 Wasm 的 PHP,最终会将移植语言 (PHP) 的垃圾回收器实现发送到已具有垃圾回收器的浏览器,这听起来很浪费。这正是 WasmGC 的用武之地。
如需详细了解 WasmGC,请参阅 WebAssembly 垃圾回收 (WasmGC) 现在在 Chrome 中默认启用。如果您想深入了解,请参阅 V8 博文一种将垃圾回收型编程语言高效引入 WebAssembly 的新方法
Wasm 尾调用优化
如果调用是从当前函数返回之前执行的最后一个指令,则该调用被称为处于尾部位置。编译器可以通过舍弃调用方帧并将调用替换为跳转来优化此类调用。这对于递归函数特别有用。例如,以下 C 函数用于对链表的元素求和:
int sum(List* list, int acc) {
if (list == nullptr) return acc;
return sum(list->next, acc + list->val);
}
对于常规调用,这会消耗 O(n) 的堆栈空间:列表中的每个元素都会在调用堆栈上添加一个新帧。如果列表足够长,这可能会导致堆栈很快溢出。通过将调用替换为跳转,尾调用优化会有效地将此递归函数转换为使用 O(1) 堆栈空间的循环:
int sum(List* list, int acc) {
while (list != nullptr) {
acc = acc + list->val;
list = list->next;
}
return acc;
}
这对于函数式语言尤为重要。它们非常依赖于递归函数,而 Haskell 等纯函数甚至不提供循环控制结构。任何类型的自定义迭代通常都会以某种方式使用递归。如果不进行尾调用优化,对于任何非琐碎的程序,都会很快遇到栈溢出问题,否则会很快耗尽栈空间。
最初,WebAssembly 不允许进行此类尾调优化,但随着尾调扩展提案的出现,这种情况发生了变化。如需了解详情,请参阅 V8 博客上的文章 WebAssembly 尾调用。
总结
现在,随着 WasmGC 和尾调用优化成为“新基准”,更多应用可以使用这些功能来提升性能,例如,Google 表格通过将 Google 表格计算工作器移植到 WasmGC 就实现了这一目标。