首页 程序笔记 WasmGC发布 浏览器可以运行Python/Java/C#

WasmGC发布 浏览器可以运行Python/Java/C#

什么是垃圾回收?

目前有两种类型的编程语言,即:自动垃圾收集的编程语言(如:Kotlin、PHP 或 Java、Python)和需要手动内存管理的编程语言( C、C++ 或 Rust)。

垃圾收集(Garbage Collection,即 GC)的主要思想是尝试回收由程序分配但不再被引用的内存。 实现垃圾收集的策略有很多,比如:引用计数,其目标是计算内存中对象的引用数量,当不再有对对象的引用时可以将其标记为不再使用,从而准备好进行垃圾回收。

将 GC 语言有效引入 WebAssembly 的两种方法

很多编程语言是用其他编程语言实现的,例如:PHP 运行时主要是用 C 实现的。可以在 GitHub 上查看 PHP 源代码,垃圾收集代码位于文件 zend_gc.c 中。

大多数开发人员会通过其操作系统的包管理器安装 PHP,但开发人员也可以从源代码构建 PHP。 例如,在 Linux 环境中,执行 ./buildconf && ./configure && make 将为 Linux 运行时构建 PHP。 但也意味着 PHP 运行时可编译为其他运行时 (Runtime),比如:Wasm。

将语言移植到 Wasm 运行时的传统方法

假如 Python 想要运行在 ARM 架构上,或者 Dart 想要运行在 MIPS 架构上 (X86、ARM、RISC-V、MIPS 是目前四大主流芯片架构),总体思路是将虚拟机重新编译为该目标架构。 如果虚拟机具有特定于架构体系的代码,例如:JIT(just-in-time) 或 AOT(ahead-of-time) ,那么开发者还需要为新体系架构实现 JIT/AOT 后端。 但是,这种方法很有意义,因为通常可以为移植到的每个新架构重新编译代码库的核心部分。

在上图中,解析器(Parser)、库支持 (Library Support)、垃圾收集器 (Garbage Collector)、优化器 (Optimizer) 等都在主运行时的所有架构之间共享,而移植到新的架构只需要一个新的后端,相比之下代码量很少。

Wasm 是一个底层编译器目标,因此也可使用传统的移植方法。比如:自从 Wasm 首次启动以来,用于 Python 的 Pyodide 和用于 C# 的 Blazor(注意,Blazor 支持 AOT 和 JIT 编译,因此是很好的示例)都在实践中得到很好的证明。 在这些情况下,语言的运行时和其他语言一样都被编译为 WasmMVP(Minimal Viable Product),因此也就可以使用 WasmMVP 的线性内存、表、函数等。

这种对这种方法的缺点和优点简单做下总结:

优点:可以重用几乎所有现有的 VM 代码,包括:语言本身的实现和优化 缺点:某些语言有自己的垃圾收集器,如:Python 等,其最终会编译成 Wasm 在浏览器中运行,此时会和 V8 自身的垃圾收集器同时存在,从而产生浪费

使用 WasmGC 将编程语言移植到新的运行时

当前的 Wasm MVP 只能处理线性内存中的数字,即: 整数和浮点数,随着引用类型 (externref) 提案的发布,Wasm 还可以保留外部引用。

WasmGC (WebAssembly Garbage Collection) 提案允许定义结构体(Struct)和数组类型(Array Heap)并执行操作,例如:创建实例、读取和写入字段、在类型之间进行转换等。 这些对象由 Wasm VM 的 GC 实现来管理,这是与传统移植方法间的核心区别。

如果将传统的移植方法理解为将一种语言移植到一种架构,那么 WasmGC 更像是将一种语言移植到一个虚拟机。WasmGC 定义了 VM 能管理的结构和数组以及用于描述其形状和关系的类型系统,移植到 WasmGC 是用这些原语表示语言构造的过程。

例如:如果想将 Java 移植到 JavaScript,可以使用 J2CL 编译器,其将 Java 对象表示为 JavaScript 对象,然后 JavaScript 对象就像所有其他对象一样由 JavaScript VM 管理。

总之,WasmGC 提案通过结构体 (Struct) 和数组堆 (Array Heap) 类型为 WebAssembly 增加了对高级语言的有效支持,使针对 Wasm 的语言编译器能够与主机 VM 中的垃圾收集器集成。 这也意味着使用 WasmGC 将编程语言移植到 Wasm 时垃圾收集器本身不再需要一起移植,即可以使用现有 VM 的垃圾收集器。

为了验证这一改进的实际影响,Chrome 的 Wasm 团队编译了来自 C、Rust 和 Java 的 Fannkuch 基准测试版本(在工作时分配数据结构)。

C 和 Rust: 二进制文件可能在 6.1 K 到 9.6 K 之间,具体取决于各种编译器标志 Java: 体积小得多,只有 2.3 K!

C 和 Rust 不包含垃圾收集器,但其仍然打包 malloc/free 来管理内存,而 Java 较小的原因是其根本不需要打包任何内存管理代码。

传统移植和 WasmGC 的不同

WasmGC 无需移植内存管理代码

在 Web 或者服务端运行时(如:Node.js、Deno 和 Bun )上,Wasm 代码是在已经具有垃圾收集器的 JavaScript 虚拟机内运行的。 此时,移植 GC 会给增加 Wasm 二进制文件不必要的大小。

事实上,这不仅是 WasmMVP 中 GC 语言的问题,也是使用线性内存语言(如 C、C++ 和 Rust)的问题,因为这些语言中执行任何分配的代码最终都会将 malloc/free 打包到管理线性内存,这需要几千字节的代码。

dlmalloc:6k+ emmalloc:速度换大小,1k+

而 WasmGC 让虚拟机自动管理内存,因此 Wasm 中根本不需要内存管理代码,既不需要 GC,也就不需要 malloc/free。

WasmGC 解决循环收集 (Cycle Collection)

在浏览器中,Wasm 经常与 JavaScript 交互( JavaScript、Web API),但在 WasmMVP(即使引用类型提案)中,无法在 Wasm 和 JS 之间建立双向链接以允许更好的收集循环。

const importObject = {imports: { imported_func: (arg) => console.log(arg) } };
WebAssembly.instantiateStreaming(fetch("simple.wasm"), importObject).then(
  (obj) => obj.instance.exports.exported_func(),
);

在这种情况下,指向 JS 对象的链接只能放在 Wasm 表中,返回 Wasm 的链接只能将整个 Wasm 实例作为单个大对象引用,如下所示:

这不足以有效收集特定的对象周期,因为一些对象位于已编译的 VM 中,一些位于 JavaScript 中。 而 WasmGC 通过定义了 VM 可以识别的 Wasm 对象,因此可以在 Wasm 和 JavaScript 之间进行正确的引用。

WasmGC 解决堆栈上的 GC 引用

GC 语言必须了解堆栈上的引用,即来自调用范围 (scope) 内的局部变量的引用,因为此类引用是保持对象存活的唯一因素。 在 GC 语言的传统移植中一直是一个问题,因为 Wasm 的沙箱阻止程序检查自己的堆栈。

对于传统方案也有一些解决方案,例如:影子堆栈(可以自动完成),或者仅在堆栈上没有任何内容时收集垃圾(这是在 JavaScript 事件循环之间的情况)。 未来也可能添加对传统方式有帮助的功能,比如:堆栈扫描支持 (Stack Scanning Support)。

目前,WasmGC 可以做到完全自动、无开销地处理堆栈引用因为 Wasm VM 负责 GC。

GC Efficiency

一个相关的问题是执行 GC 的效率,两种移植方法在这里都有潜在的优势。

传统方法可以重用现有虚拟机中针对特定语言定制的优化,例如:重点关注优化内部指针或短期对象。 而在 Web 上运行的 WasmGC 方法的优点是可以重用使 JavaScript GC 更快的所有工作,包括:分代 GC、增量收集等技术。WasmGC 还将 GC 留给了 VM, 这使得诸如高效写屏障之类的事情变得更加简单。

WasmGC 的另一个优点是 GC 可以意识到内存压力等,并可以相应地调整其堆大小和收集频率,就像 JavaScript VM 在 Web 上所做的那样。

内存碎片

随着时间的推移,尤其是在长时间运行的程序中,WasmMVP 线性内存上的 malloc/free 操作可能会导致碎片。 想象一下,总共有 2 MB 内存,而在内存的中间有一个只有几个字节的现有小分配。 在 C、C++ 和 Rust 等语言中,不可能在运行时移动任意分配,因此在该分配的左侧有近 1MB 的空间,在右侧有近 1MB 的空间。 但其是两个独立的片段,因此如果尝试分配 1.5 MB 就会失败,即使有那么多未分配的内存总量。

这种碎片会迫使 Wasm 模块更频繁地增加内存,从而导致潜在的内存不足错误。 这也是所有 WasmMVP 程序中的一个问题,包括: GC 语言的传统方案(请注意,GC 对象本身可能是可移动的,但不是运行时本身的一部分)。 而 WasmGC 有效避免了这个问题,因为内存完全由 VM 管理,VM 可以移动以压缩 GC 堆并避免碎片。

其他

Chrome 工具支持

在 WasmMVP 的传统移植中,对象被放置在线性内存中,DevTools 很难提供有用的信息,因为此类工具只能看到字节而没有高级类型 (High-level Type) 信息。

而在 WasmGC 中,VM 负责管理 GC 对象,因此可以实现更好的集成。 例如,在 Chrome 中,可以使用堆分析器 (heap profiler) 来测量 WasmGC 程序的内存使用情况。

语言熟悉

当采用重新编译虚拟机的方式时,开发者对一切都非常熟悉,这就是优势! 相比之下,使用 WasmGC 端口,开发者最终可能会考虑在语义上做出妥协以换取效率。

这是因为 WasmGC 相当于定义了新的 GC 类型(结构体和数组)作为编译目标。 因此,不能简单地将用 C、C++、Rust 或类似语言编写的 VM 编译为该形式,而只能编译到线性内存。即,WasmGC 无法最大限度利用现有 VM 代码库 。

这也意味着,在 WasmGC 移植中,开发者通常会编写新代码,将语言的构造转换为 WasmGC 原语。而是否妥协取决于特定语言的构造 (Construct) 如何在 WasmGC 中实现。

站心网

什么是垃圾回收? 目前有两种类型的编程语言,即:自动垃圾收集的编程语言(如:Kotlin、PHP 或 Java、Pyth..

为您推荐

写给那些想要自学成才的java程序员

自学java没那么难一:个人经历我的大学:第一年泡在图书馆看杂七杂八的书,跟学习毫无关系。第二年疯狂打LOL,从白银打到黄铜(黄铜守门员)。第三年上半年,被某人点醒,学习了整套C#知识体系,某马。下半年又决定..

跳槽!Java面试经验总结

0.前言笔者在不足两年经验的时候从成都一家金融科技中厂跳槽到杭州阿里淘天集团,又于今年5月份从杭州淘天跳槽到成都字节。自认为自己在面试这方面有一点心得,处于记录和分享的目的便有了此文,此文纯主观,也许对3..

Java中String类常见的方法

以下介绍字符串常见的几个方法。介绍String类在 Java 中,String类是一个代表字符串的类,具有以下特性:不可变性:String对象一旦被创建就是不可变的,即它们的值在创建后不能被更改。任何对String对象的修改操作实..

帝国CMS8.0即将发布2025年1月18号闪亮登场

一、新增支持PostgreSQL数据库和国产数据库:(支持国产数据库如:国产华为高斯(openGauss)、国产金仓数据库(kingbase)等)1、为了适配其它数据库,所有数据表查询限制数量单独函数返回,并保存在各数据库操作类文件里..

2025年最受欢迎浏览器排行榜

根据2025年的用户数据和各大评测机构的分析,以下是最受欢迎的浏览器排行榜:谷歌Chrome:凭借其高效的渲染引擎、丰富的插件支持以及与谷歌生态系统的深度整合,Chrome继续稳居市场首位。谷歌Chrome官网下载地址:ht..

微软于发布了.NET 9 Release Candidate 2 提高整体质量

微软于2024年10月8日发布了 .NET 9 的第二个也是最后一个候选版本(Release Candidate 2),标志着正式版发布前的最后阶段。主要更新内容:质量提升:专注于提高整体质量,修复已知问题,确保框架的稳定性和可靠性。..

微软发布VS Code AI工具包,集成多模型AI能力

微软发布了VS Code AI工具包,增强了代码编辑器的AI功能!微软推出了VS Code AI工具包,这是一个全新的扩展,旨在将AI功能集成到Visual Studio Code中。该工具包支持多种AI模型,包括本地和远程模型,用户可以配置自..

微软 .NET 9 正式发布!专为云原生和生成式 AI 应用设计

微软正式发布了 .NET 9,这是一次重大的版本更新。微软在 .NET 9 中带来了一系列新特性和改进,主要聚焦在性能优化、云原生支持、AI集成以及开发者体验的提升。以下是一些重要的更新亮点:性能改进:在 .NET 9 中,..

鸿蒙OpenHarmony系统可以运行跨平台的.NET Core吗?

鸿蒙(HarmonyOS)和 OpenHarmony 系统本身并不原生支持直接运行 .NET Core,但可以通过一些方法使 .NET Core 应用在其上运行。鸿蒙和 OpenHarmony 是基于 LiteOS 和 Linux 内核的多终端操作系统,因此它们的运行时..

简单优雅的Java ORM

Java的ORM框架有很多,但由于Java语言的限制大部分都不够优雅也不够简单,所以作者只能另辟蹊径造轮子了。照旧先看示例代码了解个大概,然后再解释实现原理。一、ORM示例Insertpublic CompletableFuture<Void> inser..

轻量级 JavaScript 动画库 mo.js使用教程

mo.js 是一个强大的 JavaScript 动画库,专为在网页项目中创建复杂动画和运动图形而设计。它注重提供平滑、动态的动画效果,并通过简单、模块化和灵活的组件让开发更加便捷。mo.js官网地址:https://mojs.github.io/..

Crawlee 下一代网络爬虫与浏览器自动化工具

在数字化时代,数据是企业决策和创新的基石。网络爬虫作为获取数据的重要工具,其重要性不言而喻。今天,我们将深入探讨Crawlee,一个为Node.js环境设计的先进网络爬虫和浏览器自动化库,它如何帮助开发者构建快速、..

DrissionPage 基于Python的网页自动化工具

在数字化时代,网页自动化工具成为开发人员和数据分析师的得力助手。今天,我们将深入探索一款名为 DrissionPage 的全能网页自动化工具,它以其强大的功能和优雅的语法,成为 Python 程序员的新宠。什么是 DrissionP..

.NET 9 即将推出的功能Task.WhenEach

.NET 爱好者!我刚刚偶然发现了一个非常酷的新 PR,它被合并到 .NET 运行时存储库中,我想分享一个例子。希望您能为新的 .NET 版本大肆宣传!在即将到来的 .NET 9 版本中,我们预计会有一个名为 .它在这里让您的异步..

针对 Go 语言开发的 SQL 驱动模拟库

数据库交互是几乎所有应用程序不可或缺的一部分,开发者们常常需要对数据库进行各种操作,包括插入、更新、删除和查询等。然而,在开发过程中直接对真实数据库进行操作不仅耗时耗力,还可能带来数据一致性和安全性的..

文件上传JavaScript库FilePond使用教程

传统的文件上传控件往往显得笨拙且不够用户友好。FilePond的出现,为Web文件上传带来了革命性的改变。本文将详细介绍FilePond这一JavaScript库,探讨它如何优化文件上传流程,并提供无与伦比的用户体验。什么是FileP..

DockerUI 中文可视化Docker管理工具使用示例

DockerUI 是由国内开发者打造的一款优秀的 Docker 可视化管理工具。该工具拥有简洁直观的UI界面,可以轻松进行Docker主机管理、集群管理,以及Docker任务的编排等操作。DockerUI不仅展示了资源利用率、系统信息和更..

Swapy - 开源JavaScript js拖拽插件

Swapy是一个简单易用的JavaScript工具,能够将任何布局转换为拖拽交换布局。本文将详细介绍Swapy的功能、如何使用它,以及它在实际项目中的应用。什么是Swapy?Swapy是由TahaSh开发的一款开源JavaScript工具。它的核..

Blazor的N种渲染模式原理和常见问题说明

我们从下面这幅图开始,下图显示了三种渲染模式,分别称之为静态SSR、交互式SSR(即之前的BlazorServer)、交互式CSR(即之前的BlazorWasm)。还有一种渲染模式BlazorHybrid,稍后说。一、先浅层理解一个图例静态SSR:经..

前端CSS常见的三种设计模式

CSS设计模式主要包括OOCSS、SMACSS和BEMCSS等。以下是对这些模式的具体介绍:OOCSS:面向对象的CSS,旨在编写高可复用、低耦合和高扩展的CSS代码。它将抽象(结构)和实现(样式)分离,抽离公共代码,以提高代码的..

发表回复

返回顶部