WebAssembly 3.0 全景深研报告
卷首:一句话结论
WebAssembly 3.0 是一份技术上诚实、工程上扎实、叙事上却经不起细看的标准;它在真实世界的存在感,比几乎所有人以为的低两个数量级。
若只读一段,请读这段:
3.0 带来的类型系统重构(类型化函数引用 + WasmGC)是 Wasm 自 MVP 以来最重要的一次地基工程,技术判断上无可挑剔。但——W3C 网站上根本不存在名为 wasm-core-3 的文档;两份官方文档对「3.0 含哪些特性」给出三处互相矛盾的答案;旗舰特性 Memory64 在 Chrome 全部页面加载中的占比是 0.00000146%,且在默认开启之后不升反降;3.0 引入的新异常处理 exnref,中位口径下用量仅为旧方案的 约 1/28,900(野外 99.9965% 的异常处理仍在 legacy 上;初版单日值「1/436」系末 2 天阶跃虚高,已据 30 日中位数重算,详见 §3.4、§3.10)。而坊间广为流传的「5.5% 的 Chrome 页面使用 WebAssembly」,真值是 0.0613%——差 90 倍。
这不是唱衰。这是把宣传口径、规范文本、引擎源码、真实流量四层数据摊在同一张桌上之后,它们自己呈现出来的样子。
---
第零章 · 阅读指南与证据纪律
0.1 本报告的六条纪律
1. 凡数字,只采一手源。 回溯不到出处者,宁缺毋滥,或明标 ⚠️ 存疑。
2. 凡性能与体积数据,必注明三事:测的是什么、谁测的、何时测的。三者缺一即标存疑。
3. 区分「规范支持」与「生产可用」——进入标准不等于可放心使用。
4. 区分「支持某特性」与「支持完整 3.0」——二者相差十几个大版本。
5. 凡引源码,必落当前 trunk。 二手汇总文里的源码片段常是旧版快照(本报告自己就在此栽过一次,见 §4.2.4)。
6. 推断与证据分色。 凡从曲线形态、时间巧合推出的结论,一律标为「强推断」,绝不冒充实证。
0.2 采用率的三个口径——混用即错
这是全报告最容易被误读的一处,先钉死:
| 口径 | 数值 | 量的到底是什么 | 来源 |
|---|---|---|---|
| Chrome 页面加载 | 0.0613% | 真实用户的每一次页面加载中,实际实例化了 Wasm | Chrome UseCounter,2026-08-04 |
| 网站数量 | 桌面 0.35%/移动 0.28% | 站点首页是否请求了 .wasm 资源(1620 万站点) | Web Almanac 2025 |
| 全网站点样本 | 0.05–0.1% | 含海量长尾小站的抽样 | w3techs/wmtips |
一处反直觉:Chrome 真实流量口径(0.0613%)低于站点数口径(0.35%)。原本的直觉是「Wasm 集中在高流量大站,加权后应更高」——数据推翻了它。正确解释是:即便在装了 Wasm 的站点里,也只有少数页面真正实例化它。Wasm 通常只服务于特定功能页(编辑器、播放器、地图、设计画布),而非全站。
> 故「按流量算 Wasm 无处不在」这个流行说法,站不住脚。
0.3 不可引用数字黑名单
以下数字在本报告中一律回避,并建议读者见到即警觉:
| 黑名单数字 | 流传说法 | 实况 |
|---|---|---|
| 5.5% | 「5.5% 的 Chrome 页面加载使用 Wasm」 | 已证伪。真值 0.0613%,差 90 倍。传播源自身在同一段内自相矛盾(一句写 "5.5% of websites",上一句写 "5.5% of Chrome user sessions") |
| 4.5% | 「上年 4.5%」 | 同源讹传,差 73 倍 |
| 41% | 「41% 的站点使用 Wasm」 | 荒谬。⚠️ 可能源头:Web Almanac 中「.NET 占 Wasm 模块的 41.7%」被误读(此为假说,未实证) |
| 16+ EB 可用内存 | 「Memory64 带来 16 艾字节内存」 | 层级混用。16 EiB 是核心规范的验证上限,用户实际可达的是 16 GiB,且实践中连这个都摸不到。中间隔六层,详见 §4.2 |
| Kotlin/Wasm 体积小 50–70% | 广为流传 | 查无官方出处。逐字通读 kotlinlang.org 全页无任何体积数字 |
| Coremark 95% 原生速度 | 无可溯源一手实测 | |
| Fermyon 7500 万 req/s | 无可溯源一手实测 | |
| Wasm 3.0 于 2026-06-13 落地 | 日期错误,真实日期见 §1.1 | |
| Safari 145 | 世上无此版本。Safari 用年份制版本号(26) |
0.4 本报告的盲区(必须先声明)
1. Chrome 遥测只覆盖 Chrome,且只覆盖开启 UMA 上报的用户。Firefox/Safari 无等价公开数据。 2. 只覆盖 Web。服务端 Wasm 完全不在统计内——Wasmtime/Fermyon/Fastly/Cloudflare Workers 内部的用量一概看不见。若 Wasm 的主战场已转向服务端,本报告的用量数据会系统性低估其真实重要性。 这是最大的一处盲区,读者务必记住。 3. UseCounter 统计的是「页面加载中是否触发过」,不是「触发了多少次」。 一个页面用一万次 Memory64 和用一次,计数相同。 4. 遥测不做归因。 UseCounter 不告诉你是哪些站点。凡涉及「疑似某站」的判断,均标为强推断。
---
第一章 · 版本坐标系:「Wasm 3.0」到底是什么
在讨论 3.0 有什么之前,得先弄清 3.0 是什么。这一章的四个发现,每一个都与主流叙事相左。
1.1 三个日期,别搞混
| 日期 | 事件 |
|---|---|
| 2025-09-17 | Wasm 3.0 由 W3C Wasm Community Group/Working Group 宣告完成,成为新的 live standard |
| 2026-07-28 | 核心规范渲染版页眉日期(即「今天的 3.0」实际是这一天的文本) |
| 2025-03-20 | Wasm 2.0 完成公告(注意:2.0 的技术共识早在 2022 年初就已达成) |
1.2 反直觉之一:W3C 网站上根本没有 wasm-core-3 这个文档
这不是文字游戏。3.0 的正文,物理上住在一份短名仍叫 wasm-core-2 的 Candidate Standard 文档里(版本日期 2026-07-28)。
想引用「Wasm 3.0 规范第 X 节」?你引到的 URL 里写着 2。
1.3 反直觉之二:Wasm 3.0 不是、也永远不会是 W3C Recommendation
规范主编 Andreas Rossberg 在 2.0 完成公告中的原话:
> "With the advent of 2.0, the Working Group is switching to a so-called 'evergreen' model for future releases. That means that the Candidate Recommendation will be updated in place when we create new versions of the language, without ever technically moving it to the final Recommendation state. For all intents and purposes, the latest Candidate Recommendation Draft is considered to be the current standard..."
他自己也觉得别扭,紧接着补了一句:
> "(If this sounds odd, that's mostly because the W3C's document terminology does not quite match this more flexible process, which has recently been adopted by several working groups.)"
正确表述:
- ✅ Wasm 3.0 于 2025-09-17 宣告完成,成为新的「live standard」(常青标准)
- ❌ Wasm 3.0 成为 W3C 推荐标准(Recommendation)
- Wasm 2.0:技术共识 2022 年初达成,主流实现更早发货;2024 年 12 月才进 CR;2025 年 3 月才公告。Rossberg 称延迟系「for a variety of non-technical reasons」。
- Wasm 3.0:WasmGC 在 Chrome 119(2023 年)就已发货,标准 2025-09-17 才封版。
1.4 反直觉之三:两份官方文档,对「3.0 含哪些特性」给出三处互相矛盾的答案
这是本报告最硬的一处文档考据,主编亲自逐条拉平核对。
文档一 · 提案跟踪表(finished-proposals.md,5781 字节)
标记 Spec Version = 3.0 者共 11 项:
Tail call、Extended Constant Expressions、Typed Function References、Garbage collection、Multiple memories、Relaxed SIMD、Custom Annotation Syntax、Branch Hinting、Exception handling、JS String Builtins、Memory64。
文档二 · 核心规范 Change History「Release 3.0」(75731 字节) 逐条列出 10 项: Extended Constant Expressions、Tail Calls、Exception Handling、Multiple Memories、64-bit Address Space、Typeful References、Garbage Collection、Relaxed Vector Instructions、Profiles、Custom Annotations。
差集是双向的,共三处:
| # | 特性 | 提案表 | 核心变更史 | 判读 |
|---|---|---|---|---|
| 1 | Branch Hinting | 有,标 core/3.0 | 无 | 标了 core 却未进 core 正文 |
| 2 | JS String Builtins | 有,标 core, js-api/3.0 | 无 | 标了 core 却只改了 js-api |
| 3 | Profiles | 根本没有 | 有,且唯一无脚注 | 仍 Phase 1,却已写入 3.0 正文 |
变更史十条的脚注编号依次为 7、8、9、10、11、12、13、14、(无)、15。
每一条都有脚注指向 github.com/WebAssembly/spec/tree/main/proposals/——唯独 Profiles 那条没有。
原因不难推断:Profiles 没有对应的 proposal 目录可指。它在活跃提案表里仍标 Phase 1(champion 恰是核心规范主编 Andreas Rossberg 本人)。
> 一个 Phase 1 的提案,其成果绕过了整套分阶段流程,直接落进 3.0 规范正文,且因无提案目录可引而成为全表唯一没有脚注的条目。
这不是推测,是规范文档自己的排版泄露的。
#### 公允的技术辩护(须并列呈现)
- Branch Hinting 是 custom section,核心规范的「Custom Sections and Annotations」附录本就只具名 Name Section,不逐一枚举第三方 custom section——分工上说得通。
- JS String Builtins 主体确在 js-api 层,core 侧改动极小——归类粗糙,非造假。
- Profiles 作为「语言子集约定」,性质上更像编辑框架而非新指令,走编辑通道有其合理性。
> 推论:Wasm 3.0 的「3.0」,与其说是一个有严格定义的版本,不如说是一个营销时点上的快照。问「Wasm 3.0 包含哪些特性」,两份官方文档会给你两个不同的答案。
1.5 反直觉之四:3.0 之后没有勘误表
未发现任何独立 errata 文档。 核心规范 Change History 无 3.0 之后的章节,修正随 spec main 分支滚动合入。
这正是常青模式的闭环——既然标准永远「活着」,就不需要勘误表,改了直接进正文。
但代价是透明度:
- 公告日 2025-09-17,渲染版页眉 2026-07-28
- 这中间改了什么,无从逐条枚举
- 想知道「我读的 3.0 和别人读的 3.0 是不是同一份」,只能自己 diff git 历史
> 「Wasm 3.0」既没有独立文档,也没有独立勘误表,甚至没有一个可以引用的冻结版本。它是一条持续移动的线上的一个命名。
---
第二章 · 特性全解:3.0 究竟带来了什么
先说公道话:3.0 的技术内容是扎实的,尤其类型系统那一块。批评集中在叙事与落地,不在设计本身。
2.1 类型化函数引用——真正的地基
这是 3.0 里最被低估的一项。它不是「又一个特性」,而是 WasmGC 得以成立的前提:把函数引用从 funcref(一个不透明的、只能间接调用的东西)升级为带完整签名的类型化引用 (ref $sig),从而支持 call_ref 直接调用、支持子类型、支持不可空引用。
没有它,WasmGC 的对象里就装不下有类型的方法指针,虚函数表无从谈起。
代价:验证器复杂度大幅上升。3.0 引入了递归组(rec groups)与同构递归(iso-recursive)子类型判定——这是整个 Wasm 规范里最硬核的一块,也是新引擎实现门槛陡增的主要来源。
2.2 WasmGC——最容易被外行写错的一项
先纠正一个普遍误解:WasmGC 不是「给 Wasm 加了一个垃圾回收器」。它是让 Wasm 模块能够声明结构体(struct)与数组(array)类型,并把这些对象分配在宿主的 GC 堆上,由宿主引擎(V8/SpiderMonkey/JSC)已有的 GC 统一管理。
这个区别很重要,它同时解释了 WasmGC 的两大优势与两大死穴:
优势:
- 复用宿主成熟 GC,语言实现方不必自带一个(省下巨量代码与调优工作)
- 对象活在线性内存之外,可与 JS 对象共享同一堆,跨语言引用不再需要手工桥接
- 对象活在线性内存之外,意味着你拿不到内部指针。C 风格的「指向结构体中间某字段的指针」在 WasmGC 里无法表达
- GC 策略完全不可控。你不能选代际、不能调停顿目标、不能强制回收
- 对象头有固定开销
- 闭包、弱引用、终结器全部 Post-MVP——不在 3.0 内
J2CLItableMerging.cpp),原因正是WasmGC 的类型系统表达不了 Java 式接口分发,导致每个 Java 对象被迫多背一个引用字段。通用优化器无法证明消除所需的不变量,只得由前端知识上浮注入优化器。2.3 Memory64——本报告着墨最多的一项
规范层的承诺:把线性内存的地址空间从 32 位扩到 64 位。
现实层的真相:见第四章 §4.2 的「六层落差」,以及 §4.5 的性能代价。这里先给结论,出自 SpiderMonkey 官方博客作者、memory control 提案联席主席 Ben Visness 的原话:
> "The only reason to use Memory64 is if you actually need more than 4GB of memory. Memory64 won't make your code faster or more 'modern'."
以及最锋利的一刀——同一位提案联席主席,2025-10-08 于 spec issue #1892:
> "In my opinion it would be foolish to continue to increase the memory size limit without also adding new APIs that give you better control over memory. We still don't even have a way to release memory back to the operating system when you're done with it, for example."
Wasm 至今没有把内存归还操作系统的办法。Memory64 只放大了「要」的能力,没给「还」的能力。
分量所在:此语出自提案联席主席本人,非外人挑刺。且他反对的正是「继续放大内存上限」——理由是地基未修好。时间线要害:该 issue 开于 2025-04-18,讨论延续至 2025-10-08——即 3.0 发布之后,此事仍悬而未决。
#### 一项「已完成」特性里未完工的零件:table64
Memory64 在 Phase 3 时并入了 table64。而按提案维护者自己维护的实现状态表:
| spec interp | v8/chrome | Firefox | Safari | wabt | binaryen | emscripten | |
|---|---|---|---|---|---|---|---|
| memory64 | Done | Done | Done | 「?」 | Done | Done | Done |
| table64 | Done | WIP | WIP | 「-」 | Done | Done | Done |
(表中 Safari 的那个「?」,本报告已通过 WebKit 源码解开,见 §4.3.3。)
2.4 多内存——三家引擎差 1000 倍
允许一个模块声明多块线性内存。设计意图良好(模块间隔离、动态链接、只读段分离)。
但引擎实现限制差异惊人(乙队源码核实):
| 引擎 | 多内存数量上限 |
|---|---|
| V8 | 100,000 |
| SpiderMonkey | 100 |
| JSC | 100 |
野外用量(30 日中位):占 Wasm 页面的 0.0012%,每 8.3 万个 Wasm 页面里有 1 个。初版「0.025%、每 4000 个」取自 2026-08-03 单日阶跃值,属末 2 天未定稿数据,已据中位数重算(见 §3.10)。
2.5 Relaxed SIMD 与尾调用——「起飞」是单日阶跃,不是采用曲线
先更正一处标签错误:3809 倍 这个同比并不属于尾调用。真实尾调用计数器是 V8WasmReturnCall(Chrome 112/2023-04 起默认开启),而 3809 倍 是 Relaxed SIMD(V8WasmRelaxedSimd,bucket 4767)的单日同比。
两项确实都「涨了数百倍」,但把日粒度拉出来看,都是一天之内跳上去的:
- Relaxed SIMD:2025-08-06 仅 0.00000021%,2025-08-07 单日 95× 跳到 0.00001992%,10 天内爬升后两日内又暴跌 3.5 倍。
- 尾调用(ReturnCall):2026-06-29 仅 0.00000106%,2026-06-30 单日 98× 跳到 0.00010424%,次日再翻倍,随后五周死平台。
> 结论:这两个「起飞」恰恰是反证——一个来源上线就能让曲线翻百倍,这本身就证明了盘子体量之小。具体是谁部署的,公开数据(UseCounter 无 origin/URL 维度)查不出,不做实证主张。
2.6 异常处理——六年绕一圈回到原点,而野外还停在中间那一站
这是全报告叙事最完整的一节:规范考据与遥测数据严丝合缝地扣成了一个闭环。
#### 甲、三代形态
| 代 | 时间 | 形态 | 关键动作 |
|---|---|---|---|
| Gen1 | 2017 起 | first-class exnref + 结构化 try/catch | 原始设计就有 exnref |
| Gen2 | 2020-09-15 CG | try/catch/delegate/rethrow | 移除 exnref(理由:不利两阶段展开) |
| Gen3 | 2023-10 CG,即 3.0 | try_table + throw_ref + exnref 为堆类型 | 重新引入 exnref |
exnref。#### 乙、为何改回来(issue #281,规范级逐字理由)
1. try_table 与 block/branch 语义一致——回归 Wasm 的结构化控制流本形
2. opcode 数量减半
3. VM 解码更简单
4. delegate 可被 exnref + branch 完全取代
5. rethrow 改用显式 exnref,避免依赖词法上下文——后者曾长期困扰编译器做控制流变换
五条理由条条成立。这不是朝令夕改,是设计上的诚实回退。 该给的公允评价必须给。
#### 丙、但代价是永久的
issue #281 的 Migration Plan 逐字写着:
> "VMs 将长期同时支持新旧两套指令……弃用可能不可行(deprecation may not be feasible)"
规范作者自己写下「可能永远删不掉」。
#### 丁、遥测把这个闭环钉死
| 指标 | 值(单日,2026-08-04) | 值(30 日中位口径) |
|---|---|---|
V8WasmExceptionHandling(Gen2 legacy) | 0.00657% | 0.00607324% |
V8WasmExnRef(Gen3,即 3.0) | 0.0000151% | 0.00000021% |
| 倍数差 | 436 倍 | 约 28,900 倍 |
野外约 99.9965% 的 Wasm 异常处理,跑在 Gen2 上(中位口径)。
时间序列更说明问题:exnref 在 2025-09-17(3.0 公告日)仅 0.00000011%,2026-01-01 跌至 0.00000001%,2026-08-02 仍仅 0.00000027%,直到 2026-08-03/04(数据集最后 2 天)单日阶跃到 0.0000151%——该阶跃尚未定稿,中位口径下它仍趴在 0.00000021% 量级。
旁证:Kotlin/Wasm 官方文档明确要求浏览器支持 "garbage collection and legacy exception handling"。一门 2024 年后才发布 Wasm 后端的现代语言,官方要求的仍是 legacy。
> 一句话总结:规范用六年绕了一圈回到起点,而真实世界的代码,99.9965% 还停在中间那一站(中位口径)——并且按规范作者自己的说法,那一站可能永远拆不掉。
2.7 Relaxed SIMD × 确定性 Profile——3.0 最值得称道的设计决策
先更正一处我自己的误判。 本研究初期,我把「3.0 同时纳入 Relaxed SIMD(非确定性)与 Deterministic Profile(确定性)」列为自相矛盾之处。甲队查规范原文后证明:二者不矛盾,是我问错了。
profiles.html 原文:
> "All relaxed vector instructions have a fixed behaviour that does not depend on the implementation."
机制:DET profile 不禁用 relaxed SIMD,而是把每条 relaxed 指令钉死到规范预先定义的那个固定语义上。
- full profile 下:relaxed 指令行为因硬件而异(这才是它「relaxed」的意义——让引擎选最快的那条机器指令)
- DET profile 下:同样的指令,语义被锁定为规范指定的那一种
但须点出一处现实落差:DET profile 全表仅 Wasmtime 一家标 ✅,且无任何实测案例材料。
> 设计漂亮,落地为零。
2.8 其余各项速览
| 特性 | 归属 | 一句话 |
|---|---|---|
| 扩展常量表达式 | 3.0 | 允许在 global/data segment 初始化里做简单算术。野外用量 0(整整为零) |
| 自定义注解语法 | 3.0 | 文本格式层的 (@name ...) 注解语法,工具链友好,不影响语义 |
| JS String Builtins | 3.0(归属存疑,见 §1.4) | 让 Wasm 直接调用 JS 引擎内建的字符串操作,避免跨界拷贝。对 WasmGC 语言至关重要 |
| Branch Hinting | 3.0(归属存疑,见 §1.4) | custom section,向引擎提示分支概率 |
| Profiles | 3.0(Phase 1 却已入正文,见 §1.4) | 语言子集约定机制。已定义两个:full 与 deterministic |
2.9 一份必要的澄清:这些不在 3.0 里
流传的错报太多,逐条钉死:
| 特性 | 真实状态 | 常见误报 |
|---|---|---|
| Threads | Phase 4,未随 3.0 定稿 | 「Wasm 早就有线程了」 |
| Shared-Everything Threads | Phase 1 | 常与上一条混为一谈 |
| Stack Switching | Phase 3 | 「3.0 真正新增的是 Stack Switching」——错,Phase 3 按定义不可能在 3.0 内 |
| JSPI(JS Promise Integration) | Stage 4 已标准化,但明确不属于 Wasm 3.0 | 常被算进 3.0 |
| Component Model | 不在 3.0 核心 | 「3.0 带来了组件模型」 |
| Custom Descriptors | Stage 3,不在 3.0 | Scala.js 正因它未落地而被迫静默忽略 @JSExport |
线程缺席不是委员会不想推。遥测里那条「峰值 2018-08-11、八年来负增长、今日仅当年三分之一」的曲线,其实是 asm.js(UseAsm,bucket 473)——它的时序始于 2014 年,WebAssembly 尚不存在,不可能是 Wasm 线程。真正的线程计数器是 bucket 2533 V8WasmThreadOpcodes(0.00304864%)与 2532 V8WasmSharedMemory(0.00250813%),本次未拉取时序。而 Wasm 线程(SharedArrayBuffer)在 Web 上长期低迷,根因是 Spectre/Meltdown 之后被跨源隔离(COOP/COEP)锁死——这条路被安全模型堵死了大半。
---
第三章 · 独家数据:Chrome 官方遥测下的 Wasm 3.0
> 本章为本报告独家资产。 数据由主编于 2026-08-06 直接从 Chrome Platform Status 数据 API 拉取全部 13 条完整时序,本地解析汇总。全网中文报道未见同源数据。
>
> 数据源:https://chromestatus.com/data/timeline/featurepopularity?bucket_id=
> 口径:day_percentage = 当日触发该 UseCounter 的页面加载数 ÷ Chrome 全部页面加载数
> 最新数据日:2026-08-04(3.0 定稿后 10 个半月)
3.1 主表:Wasm 相关 UseCounter 全景
| 计数器(bucket_id) | 最新值(% 页面加载) | 历史峰值 | 峰值日 | 首次出现 |
|---|---|---|---|---|
WebAssemblyModuleCompilation(4689) | 0.06253866 | 0.07161315 | 2026-03-18 | 2023-10-18 |
WebAssemblyInstantiation(2237) | 0.06130152 | 0.06855333 | 2026-05-13 | 2017-11-22 |
V8WasmExceptionHandling(3800,legacy EH) | 0.00657123 | 0.01133694 | 2026-06-09 | 2021-03-11 |
V8WasmTypedFuncRef(4770) | 0.00528021 | 0.01005973 | 2026-06-09 | 2024-01-19 |
V8WebAssemblyJSStringBuiltins(4671,归属存疑,见 §2.8) | 0.00527280 | 0.01004864 | 2026-06-09 | 2023-11-19 |
V8WasmGC(4617,3.0) | 0.00526691 | 0.01006660 | 2026-06-09 | 2023-08-10 |
V8WasmCustomDescriptors(5651,非 3.0 未标准化) | 0.00506035 | 0.00509911 | 2026-08-03 | 2025-12-11 |
UseAsm / asm.js(473,非 3.0) | 0.00243728 | 0.00709288 | 2018-08-11 | 2014-07-03 |
V8WasmRelaxedSimd(4767,3.0) | 0.00072367 | 0.00078639 | 2026-07-10 | 2024-01-30 |
V8WasmReturnCall(4765,3.0,尾调用) | 0.00025448 | 0.00027074 | 2026-07-12 | 2024-01-25 |
V8WasmMultiMemory(4616,3.0) | 0.00001533 | 0.00001533 | 2026-08-04 | 2023-08-30 |
V8WasmExnRef(4769,3.0 新 EH) | 0.00001508 | 0.00001508 | 2026-08-04 | 2024-02-21 |
V8WasmMemory64(4615,3.0) | 0.00000146 | 0.00000800 | 2024-10-13 | 2023-09-14 |
bucket_id 与 property_name 取自 Chrome 官方数据字段本身(非人工对应);详见 §3.10 口径审计。真正的 JSPI 计数器为 bucket 4760 V8WasmJavaScriptPromiseIntegration(0.00071892%,未在本表时序内)。3.2 换算表:占「使用 Wasm 的页面」的比例
以 WebAssemblyInstantiation = 0.06130152% 为分母,这个视角比绝对值更能说明问题。多内存与 exnref 因末 2 天阶跃,改用 30 日中位口径(分母中位 0.05732982%)。
| 特性 | 占全部页面加载 | 占 Wasm 页面 | 直观说法 |
|---|---|---|---|
| legacy EH(2.0 时代) | 0.00657% | 10.7% | 每 9 个 Wasm 页面有 1 个 |
| WasmGC(3.0) | 0.00527% | 8.6% | 每 12 个有 1 个(且疑似集中于个别大站) |
| 类型化函数引用(3.0) | 0.00528% | 8.6% | 每 12 个有 1 个 |
| JS String Builtins(非 3.0) | 0.00527% | 8.6% | 每 12 个有 1 个 |
| Custom Descriptors(非 3.0,未标准化) | 0.00506% | 8.3% | 每 12 个有 1 个 |
asm.js / UseAsm(非 3.0) | 0.00244% | 4.0% | 每 25 个有 1 个 |
| Relaxed SIMD(3.0) | 0.00072% | 1.18% | 每 85 个有 1 个 |
尾调用 return_call(3.0) | 0.00025% | 0.42% | 每 240 个有 1 个(2026-06-30 单日阶跃,非广泛采用) |
| Memory64(3.0) | 0.00000146% | 0.0024% | 每 4 万个有 1 个 |
| 多内存(3.0) | 0.0000153% | 0.0012% | 每 8.3 万个有 1 个(30 日中位,末 2 天阶跃未定稿) |
exnref(3.0 新 EH) | 0.0000151% | 0.00037% | 每 27 万个有 1 个(30 日中位,末 2 天阶跃未定稿) |
| 扩展常量表达式(3.0) | 0 | 0 | 整整为零 |
3.3 第一记:「5.5%」是讹传,差 90 倍
真实值 0.0613%。不是 5.5%,不是 4.5%,是万分之六。
这个数字在中英文技术文章中被反复引用,甚至进入了若干「Wasm 已成主流」的论证链条。它与 Chrome 官方遥测相差 90 倍。
一年趋势:WebAssemblyInstantiation 从 2025-08-04 的 0.05115% 涨到今日 0.06130%,同比 1.20 倍。有增长,但基数是万分之五。按这个斜率,Wasm 要摸到 1% 的页面加载,还需要约 15 年。
3.0 有没有带来拐点?没有。
| 日期 | 值 | 相对前档 |
|---|---|---|
| 2023-09-01 | 0.0295% | — |
| 2024-09-01 | 0.0473% | +61% |
| 2025-03-20(2.0 公告日) | 0.0459% | −3% |
| 2025-09-17(3.0 公告日) | 0.0501% | +9% |
| 2026-01-01 | 0.0533% | +6% |
| 2026-04-01 | 0.0603% | +13% |
| 2026-08-04 | 0.0613% | +2% |
3.4 第二记(最锋利):3.0 的新异常处理,在野外几乎不存在
旧 EH 0.00657123% vs 新 exnref 0.00001508%,单日相差 436 倍;按 30 日中位数口径(legacy 0.00607324% ÷ exnref 0.00000021%)实为 约 28,900 倍。
Wasm 3.0 定稿已 11 个月,Chrome 137 默认开启 exnref 也已数月。结果是:几乎所有真实世界的 Wasm 异常处理代码,仍跑在被 3.0 取代的那套旧提案上。
V8WasmExnRef 今日值 = 历史峰值,说明它还在爬坡起点——但那个起点(单日值,取自末 2 天阶跃)低到每 4000 个 Wasm 页面才有 1 个;按 30 日中位口径,实为每 27 万个才有 1 个(见 §3.10)。
3.5 第三记:Memory64 的用量,今天比两年前还低
- 2023-09-14 首次出现(flag 试验期)
- 2024-10-13 达到历史峰值 0.000008%(仍在 flag 期)
- 2025 年 Chrome 133 默认开启 Memory64
- 2026-08-04 用量 0.00000146%,只有峰值的 18%
这与引擎侧查实的机理完全吻合:Memory64 是负收益特性(10%–100% 性能回退,引擎方自述「唯一使用理由是你确实需要超过 4GB」)。
一个对照,读来颇有意味:同日,2013 年的老古董 asm.js 的使用率,是 Memory64 的 1670 倍。
3.6 第四记:「起飞」是单日阶跃,不是采用曲线
> 本节在口径审计后重写。原稿误将 3809 倍 归给尾调用——该数字实为 Relaxed SIMD(V8WasmRelaxedSimd,bucket 4767)的单日同比。真实尾调用计数器是 V8WasmReturnCall(bucket 4765)。
| 计数器(bucket_id) | 2025-08-04 | 2026-08-04 | 单日同比 | 中位口径同比 |
|---|---|---|---|---|
V8WasmRelaxedSimd(4767) | 0.00000021% | 0.00072367% | 3809 倍 ⚠️ | 6850× |
V8WasmReturnCall(4765,尾调用) | 0.00000008% | 0.00025448% | 3181 倍 | 4227× |
V8WasmExnRef(4769) | 0.00000008% | 0.00001508% | 189 倍 ⚠️ | 4.2× |
V8WasmMultiMemory(4616) | 0.00000046% | 0.00001533% | 33 倍 ⚠️ | 1.4× |
V8WasmGC(4617) | 0.00485171% | 0.00526691% | 1.09 倍 | 1.2× |
V8WasmMemory64(4615) | 0.00000113% | 0.00000146% | 1.29 倍 | 1.4× |
exnref/多内存的中位口径同比仅 4.2×/1.4×,Relaxed SIMD 的 3809 倍在改用阶跃后基准日后跌至 7.7 倍。这些都证明:曲线是单日阶跃,不是采用曲线。反过来看这张表最重要的一行:WasmGC 同比仅 1.09 倍(中位 1.2×)——已进入平台期,不再增长。
3.7 第五记(强推断):WasmGC 的野外用量,基本就是 Google 自己
请看这三个数字:
V8WasmTypedFuncRef 0.00528021% 峰值 0.01005973% 峰值日 2026-06-09
V8WebAssemblyJSStringBuiltins 0.00527280% 峰值 0.01004864% 峰值日 2026-06-09
V8WasmGC 0.00526691% 峰值 0.01006660% 峰值日 2026-06-09
三条独立计数器,当前值在小数点后第 5 位才分岔,峰值日完全相同,峰值量级也几乎相同。更狠的是:三条曲线在 2026-06-09 同时见顶,随后同时腰斩(从 0.0100% 跌到 0.0053%)。
三个独立特性同步腰斩,只能是同一个流量源发生了变化。
唯一合理的解释:这三项特性主要由同一批页面同时触发。而「同时使用 WasmGC + 类型化函数引用 + JS String Builtins」的技术栈画像,正是 J2CL 编译的 Google Sheets/Docs。
> ⚠️ 此为强推断,非实证。 UseCounter 不告诉你域名。但这条观察本身已足够重要: > 「WasmGC 已被 0.0053% 的 Chrome 页面使用」这句话,不等于「WasmGC 被广泛采用」——它更可能等于「有一个大站在用 WasmGC」。
3.8 第六记:Google 在野外跑还没标准化的东西
V8WasmCustomDescriptors = 0.00506%,与 WasmGC 几乎同量级。而 Custom Descriptors 不属于 3.0,仍在推进中(Stage 3)。
> 一个未标准化的提案,其野外使用率是已标准化 Memory64 的 3466 倍。
反证成立:真正决定野外用什么的,不是标准化流程,而是有没有一家大厂在自家产品里用它。
(呼应第五章:Scala.js 正因 Custom Descriptors 未落地而被迫静默忽略 @JSExport——同一个提案,Google 已在生产里跑,别家还在等。)
3.9 第七记(预堵反驳):不是开发者不愿用新特性
有一个懒惰的辩护随时可能出现:「3.0 特性用量低,只是因为还早,生态需要时间。」
Custom Descriptors 用事实反驳了它——而且比 JSPI 更锋利。
V8WasmCustomDescriptors(bucket 5651):2025-12-11 首次出现(0)→ 2026-08-04 达到 0.00506035%。
八个月,从零到 0.0051%。 而 V8WasmGC 从 2023-08 首现算起,花了三年才到 0.0053%。Custom Descriptors 的爬坡速度是 WasmGC 的 4.5 倍。
而 Custom Descriptors 不属于 Wasm 3.0——它至今仍是 Stage 3 的未标准化提案。
> 当一个特性真正被大厂用进自家产品(Google 已在生产里跑 Custom Descriptors),八个月就能起量——哪怕它还没标准化。3.0 特性用量低,不是因为时间不够,是因为它们没击中大多数 Wasm 开发者当下的痛点,也没有一家大厂非用不可。
3.10 遥测口径审计与四条纪律
> 本节为第三轮核查补入。主编回溯 05-telemetry.md 时发现:13 条 bucket_id→计数器名的人工对应里 5 条标错。三级互证定案:① 原始 data/*.json 每点自带 property_name 字段;② Chrome 官方全量清单 chromestatus.com/data/featurepopularity 的 5,882 条记录;③ V8 源码 include/v8-isolate.h 枚举 UseCounterFeature 根本不存在 kWasmTailCall。5 条标错已据权威数据源全量更正(见 §3.1、§3.2、§2.5、§2.9),本节约其方法论。
四条口径纪律(本报告终稿操作结论):
1. 凡引 Chrome UseCounter,必同时给 bucket_id 与 property_name,且 property_name 取自数据自身字段,不人工对应。
2. 低位计数器(< 0.001%)一律用 30 日中位数,禁单日值,并注明窗口;单日值只用于描述阶跃事件本身。
3. 序列末 3 天视为未定稿,不作头条数字(exnref/多内存的 2026-08-03 跳变即例证)。
4. 凡称「同比 N 倍」,必查基准日前后 ±7 天有无阶跃;有则改中位口径或直接改述为「某日发生阶跃」。
本轮更正要点一览: 3809 倍 实为 Relaxed SIMD 单日同比(非尾调用);尾调用真计数器 V8WasmReturnCall 中位同比 4227×、exnref 中位同比仅 4.2×(非 189×)、多内存中位同比 1.4×(非 33×);新旧异常处理倍数差中位约 28,900 倍(非 436×),legacy 占比 99.9965%(非 99.8%)。
---
第四章 · 引擎实测:从规范文本到机器码
4.1 支持矩阵:「支持某特性」与「支持完整 3.0」是两回事
「完整支持 Wasm 3.0」的真实门槛(取自 Scala.js 官方工程判断,其 Wasm 后端明确要求「A Wasm engine with support for Wasm 3.0」):
| 引擎 | 最低版本 |
|---|---|
| Chrome | 137 |
| Firefox | 134 |
| Safari | 26(年份制版本号) |
| Node.js | 25 |
顺带钉死一处讹传:世上没有「Safari 145」这个版本,那是表格抽取错位所致。
4.2 Memory64 的六层落差(本报告核心洞见)
官方宣传吹的是「16 exabytes」,用户实际站在何处?中间隔着五层塌方。
| 层 | 上限 | 出处(均经亲验) |
|---|---|---|
| ① 核心规范(验证) | 2⁴⁸ 页 = 16 EiB | memory64 Overview 验证规则 |
| ② SM/JSC 声明上限 | 2³⁷−1 页 ≈ 8 PiB | WasmConstants.h/WasmLimits.h |
| ③ V8 架构天花板 | 128 TB | wasm-limits.h L103–108,页号仍 32 位 |
| ④ JS-API 规范(增长) | 16 GiB | js-api #limits |
| ⑤ V8 实际(64 位宿主) | 262,144 页 = 16 GiB | kSpecMaxMemory64Pages |
| ⑤′ V8 实际(32 位宿主) | 32,767 页 = 2 GiB | 与 memory32 完全相同 |
| ⑥ 实践可达 | 连 16 GiB 都摸不到 | eqrion,spec issue #1892 |
V8 源码 wasm-limits.h 第 103–108 行,原文照录:
// Maximum number of pages we can allocate, for memory32 and memory64. This
// might be lower than the number of pages that can be declared (e.g. as
// maximum): kSpecMaxMemory{32,64}Pages.
// Even for 64-bit memory, the number of pages is still a 32-bit number for now,
// which allows for up to 128 TB memories (2**31 * 64k).
static_assert(kV8MaxWasmMemory64Pages <= kMaxUInt32);
「the number of pages is still a 32-bit number for now」——V8 自承:即便是 64 位内存,页计数至今仍是 32 位整数。这道架构天花板是 128 TB,距核心规范的 16 EiB 差 12.8 万倍。
这句 "for now" 才是真正的 TODO,只是没写成 TODO 的形式。
#### 4.2.2 层⑤′(最辛辣):32 位宿主上用 Memory64,收益恰为零
同一文件第 42–47 行(数字分隔符为 C++14 单引号写法):
constexpr size_t kV8MaxWasmMemory32Pages = kSystemPointerSize == 4
? 32767 // = 2 GiB - 64 KiB
: 65536; // = 4 GiB
constexpr size_t kV8MaxWasmMemory64Pages = kSystemPointerSize == 4
? 32767 // = 2 GiB - 64 KiB
: 262144; // = 16 GiB
在 32 位宿主上,memory32 与 memory64 的上限完全相同,都是 32,767 页 = 2 GiB − 64 KiB。
意即:在 32 位设备上(大量 IoT、低端与老旧 Android),启用 Memory64 一个字节的额外内存都拿不到,却要照付 10%–100% 的边界检查代价。纯负收益,无任何补偿。
#### 4.2.3 层⑥:浏览器厂商自承,大内存路径测试覆盖不足
eqrion(Ryan Hunt,SpiderMonkey/Firefox,JS String Builtins champion),2025-10-03:
> "SpiderMonkey/Firefox shares the same restriction (taken from the JS-API) that you cannot ever grow a wasm memory above 16GiB. In practice it's unlikely that you'll be able to even grow to 16GiB. On 32-bit systems we have a much lower limit, and 16GiB of memory is relatively rare in the Firefox user base... It also really makes browsers tests of large memories difficult to do, as we've noticed that our test runners get OOM killed frequently once they start using that much memory."
弦外之音极重:这已非「限制」,而是「未经充分验证的代码路径」。
#### 4.2.4 一处自我纠错,记录在案
本研究初期,主编据二手转载下发过两条「源码证据」,均被乙队查当前 trunk 后推翻:
1. ~~V8 注释「TODO(clemensb): ... For now, use 16GB」自承临时~~ → 该注释在当前 V8 源码中已被删除,替换为指向 js-api 规范的说明。V8 现在是引用规范,非临时凑数。
2. ~~SpiderMonkey MaxMemory64LimitField = uint64_t(1) << 48~~ → 当前 WasmConstants.h 中不存在此常量,全文检索无匹配。它大概率出自一篇二手汇总文的旧版快照。
教训已写入本报告纪律第 5 条:二手汇总文里的源码片段可能是旧版快照,凡引源码必须落到当前 trunk。
> 口径定则:凡出现 EB 量级数字,必须标明「此为①核心规范层,非用户可用内存」。宣传取①,用户拿⑥,中间隔着五层。
4.3 三家引擎的实现限制,差异远超想象
Wasm 号称「一次编译,处处运行」,但各引擎的实现限制常量差异极大,且合规测试套件查不出来。
| 限制项 | V8 | SpiderMonkey | JSC | 备注 |
|---|---|---|---|---|
| 多内存数量上限 | 100,000 | 100 | 100 | 差 1000 倍;跨引擎安全值是 100 |
| GC 子类型深度 | 63 | 63 | 63 | 三家锁死一致 |
| GC 数组载荷上限 | 无等价常量 | 1,987,654,321 | 2³⁰ | 三家口径全不同 |
| 子类型-超类型数 | — | — | maxSubtypeSupertypeCount = 1 | JSC 只允许单一超类型 |
1. 多内存差 1000 倍——3.0 的旗舰特性之一,在 V8 上能开 10 万个,在另两家只能开 100 个。按 V8 的余量设计,换浏览器即崩。 2. GC 数组载荷上限三家不同,且合规测试查不出来——真实的大数组场景会出现跨浏览器不一致,而开发者拿不到任何预警。
GC 子类型深度 63 处,V8 注释自承:
> "The limits are agreed upon with other engines for consistency."
一个有意思的对照:深度限制三家协商一致,数组载荷上限却各行其是。 说明「引擎间一致性」是逐项谈判的结果,而非规范强制。深类型树的语言(某些 Scala/Kotlin 继承链)会撞上 63 这堵墙。
#### 4.3.1 Wasmtime 的 GC:「支持了」和「能用」之间的距离
Wasmtime 源码里的收集器枚举:
enum Collector { Auto, DeferredReferenceCounting, Null, Copying }
Copying的源码注释写着 "not yet functional"DeferredReferenceCounting漏循环引用(引用计数的经典短板)Null是「不回收」
#### 4.3.2 源码里的未来:引擎已跑在标准前面
- SpiderMonkey 已有
ENABLE_WASM_COMPONENTS段 → 组件模型正在进浏览器 - SpiderMonkey 已有
ENABLE_WASM_CUSTOM_PAGE_SIZES - V8 已含 Custom Descriptors 常量(与遥测显示的 0.00506% 使用率吻合)
#### 4.3.3 Safari 的 Memory64 问号,本报告予以解开
提案维护者在实现状态表里给 Safari 的 memory64 打了个「?」。三条 WebKit 侧一手证据互证:
1. Bugzilla 300538 仍为 NEW 状态
2. trunk commit 0d0080ea(2026-07-30):useWasmMemory64 flag 默认开启
3. JSC 源码 WasmLimits.h 中确有 maxMemory64Pages = (1ULL << 37) - 1,代码在树内
> 结论:Safari 的 memory64 实现基本就绪,但未随正式版发货。定性为「实现就绪,未发货」。
补刀(乙队×丁队交叉质证):开发者今天仍不能依赖这三项,部分原因正是社区赖以判断「落地了没」的官方表本身已失修——webassembly.org/features 的 Safari 行把 Relaxed SIMD 所需开关写成 useWebAssemblyRelaxedSIMD,而 WebKit 源码真名是 useWasmRelaxedSIMD,连开关名都写错。归因须改:不是「Apple 不做 3.0」,而是「trunk 已实现、正式版发布滞后、特性表失修」。这一条亦写入 §6 的可证伪条件。
4.4 Memory64 的性能代价:机理与机器码
代价范围:10% 至超过 100%(2 倍减速),取决于负载。出自 SpiderMonkey 官方博客(Ben Visness,2025-01-15):
> "WebAssembly apps tend to run slower in 64-bit mode than they do in 32-bit mode... it can range from just 10% to over 100%—a 2x slowdown just from changing your pointer size. This is not simply due to a lack of optimization."
机理(本报告认为这是理解 Memory64 的关键):
1. 64 位机器实际多为 48 位寻址,每进程有 281 TB 地址空间 → 地址空间极廉价 2. 浏览器遂为每一个 32 位 Wasm 模块预留满 4 GB 地址空间(首 64 KB 可读写,其余预留但不可访问) 3. 32 位指针最大值 2³²−1,物理上不可能越出这 4 GB → 全部边界检查可以删除 4. Memory64 享不到此福:Wasm 地址空间与宿主同样大小,无处预留 → 每次访存必须付边界检查
边界检查的实际代价,SpiderMonkey 为一条 i32.load 生成的 x64 机器码:
movq 0x08(%r14), %rax ;; 从实例载入内存长度
cmp %rax, %rdi ;; 地址与上界比较
jb .load ;; 合法则跳转
ud2 ;; 否则 trap
.load:
movl (%r15,%rdi,1), %eax ;; 真正的载入 —— 32 位下只有这一行
32 位下只有最后一行。其余四行是 Memory64 的固定税。
作者本人的结论,值得整段抄给每一个正在考虑升级的人:
> "The only reason to use Memory64 is if you actually need more than 4GB of memory. Memory64 won't make your code faster or more 'modern'."
> 故凡标 Memory64「已支持」处,必须加注「支持 ≠ 无代价」。
幅度须按「引擎 × 架构」写明,不能写单一数字。 乙队补查源码:V8 现已对 memory64 用 trap handler,每次访存代价降到「一条无符号比较」——但仅限 ARM64 / x64 / RISCV64 / LOONG64,其余架构强制回落完整边界检查;SpiderMonkey 的免检策略源码明写只给 32 位 memories(memory32 不受影响)。故「memory64 慢」方向成立,幅度是引擎与架构的函数;写「10%–100%」会被打——那是 SpiderMonkey 单引擎的测量值。
最阴的失败模式:受限容器里静默降级。 Node/V8 需为每个 memory64 实例预留 16 GiB 虚拟地址空间 cage;Node 文档明写,启动时若虚拟内存不足(ulimit -v 受限、部分 K8s/serverless 配置)会自动关闭 trap handler、改用内联边界检查——功能正常、性能悄悄变差、无任何警告。这种退化永不在任何性能评测里出现,因为评测都在裸机跑。部署到容器/serverless 之前,先验 ulimit -v。
---
第五章 · 生态格局:谁在用,用出了什么
5.1 一个反转:全网第一大 Wasm 语言,恰是拒绝 WasmGC 的那一家
Web Almanac 2025 对 1620 万站点的爬取,给出了野外 Wasm 模块的源语言分布:
| 源语言 | 桌面份额 | 移动份额 |
|---|---|---|
| .NET / Mono(含 Blazor) | 41.7% | 38.7% |
| Unknown(元数据被精简剥离) | 35.7% | 27.2% |
| Emscripten(C++) | 10.1% | 7.8% |
| Scala | 3.6% | 3.4% |
| 其余(含 Rust、Go、Kotlin、TeaVM 等) | 合计约 8.9% | — |
这条事实同时打击两个方向的流行叙事:
1. 打「WasmGC 是 Wasm 的未来」:现实中最大的 Wasm 语言阵营已明确出局,且它靠线性内存 + 自带 GC 的老路子拿下了四成份额。 2. 也打「Wasm = Rust/C++ 的天下」:Emscripten C++ 只有 10.1%,Rust 甚至没能拿到单独的份额条目(很可能大量落在 35.7% 的 Unknown 里,因 Rust 产物常被 wasm-opt 精简)。
一条反向信号:Almanac 2025 这一版专门新增了 Kotlin、Go & TinyGo、TeaVM 的识别器——但这三类在最终的语言分布里一个数字都没报出来,而 .NET、Emscripten、Scala 三家都报了。
⚠️ 这是从「报告未列出」反推的软证据,Almanac 并未明说 Kotlin 为零。但它与「Kotlin/Wasm 官方从未发布任何体积或性能数字」相互印证,共同支撑一个较弱但安全的命题:Kotlin/Wasm 缺乏野外部署证据。
5.2 三条互证(其中一条我原本弄错了,已改)
主编原拟三条「Chrome 遥测 × Web Almanac」交叉验证。丙队复核后驳回其中一条,理由成立,此处按修正后的版本呈现。
#### ✅ 互证一(成立):野外头号用户拒绝了 3.0 的头号特性
| 侧面 | 数据 | 源 |
|---|---|---|
| 谁在用 Wasm | .NET/Mono + Blazor 占 Wasm 模块 41.7%,全网第一 | Web Almanac 2025(站点爬取) |
| 这一家的态度 | 公开表态当前 WasmGC 不足用,不予采用 | dotnet/runtime #94420(官方 issue) |
| 头号特性的实测用量 | V8WasmGC 0.00527%,同比仅 1.09 倍(平台期) | Chrome UMA 遥测(浏览器上报) |
#### ✅ 互证二(成立):采用率两口径互相解释
站点口径 0.35% 与流量口径 0.0613%,通过「站点数 vs 流量权重」完整调和,且方向反直觉——站点 > 流量,说明 Wasm 散布于长尾普通站点,而非集中于少数巨头。
这一条同时纠正了本研究初期的一个判断。丙队原稿与主编原稿都曾假设「Wasm 集中在高流量大站」,数据把它推翻了。
#### ❌ 互证三(不成立,予以撤销)
主编原拟第三条:「Web Almanac 与 Chrome 遥测双源印证 3.0 特性野外近零」。丙队驳回,理由充分:
- Chrome 遥测:测了 → 得零。有信息量。
- Web Almanac:根本没测(其原文自述 "the features discussed here are limited and do not cover all the features WebAssembly supports")→ 无数字。零信息量。
这条纪律已写入本报告总则:
> 多源互证的前提是每个源都独立地产生了信息。把「沉默的源」算作一票,是交叉验证最常见的伪造方式。 本报告既然要打二手数据的假,自己更不能踩这个坑。
结论本身不变(3.0 特性野外用量近零),但依据改为Chrome 遥测单一源——口径明确、时序完整、量级悬殊到不需要帮手。
#### ✅ 而 Almanac 的真实贡献,是一条更有价值的反向对照
Almanac 真正测了的两项,都是 2.0 时代的特性,而且铺得很开:
| 特性 | 桌面模块数 | 归属 |
|---|---|---|
| Bulk Memory | 187,674 | Wasm 2.0 |
| Sign Extension | 45,969 | Wasm 2.0 |
exnref 停在每 4000 个 Wasm 页面 1 个。
> 差别不在生态的采用意愿,而在特性本身。⚠️ 两个数字不同量纲(模块数 vs 页面加载占比),仅作定性对照,不可换算,且两源时间点不同(2025-07 vs 2026-08)。
5.3 WasmGC 语言全景:谁上了车,谁没上
| 语言 | 状态 | 关键限定 |
|---|---|---|
| Java / J2CL | ✅ 唯一旗舰生产案例 | Google Sheets,见 §5.5 |
| Kotlin/Wasm | ⚠️ 官方口径仍是 Beta | 官方无任何体积/性能量化数字;要求浏览器支持 legacy EH |
| Dart / Flutter Web | ✅ 跑通 | 有致命盲区(见丙队原卷) |
| Scala.js | ✅ Stable | 体积约为 JS 后端的 2 倍 |
| OCaml / wasm_of_ocaml | ✅ 被低估的赢家 | — |
| Scheme / Hoot | ✅ | 小众 |
| C# / .NET | ❌ 明确不采用 | issue #94420 |
| Go | ❌ 明确不采用 | 自带运行时与 GC |
| Python / Ruby | — | 与 WasmGC 无关,走线性内存路线 |
| Swift | ✅ 官方支持 | 走 WASI 路线,非 WasmGC |
5.4 「WasmGC 让包更小」——伪命题,取决于基线
体积表现不是 WasmGC 的属性,而是「你原先那份 JS 产物有多臃肿」的属性。
本研究初期设想的构图是「Kotlin 小 50–70% vs Scala.js 大 2 倍,两者皆真、基线不同」。丙队逐字通读官方文档后,前半截被推翻:
kotlinlang.org 的 Wasm 概览页(改于 2026-05-18)全页没有任何体积数字。仅有一节谈性能,措辞三重限定——"still in Beta"、"encouraging performance traits"、"outperforms JavaScript and is approaching that of the JVM",并注明结果 "come from our testing in a recent version of Google Chrome"。全无量化。
> 故「Kotlin/Wasm 体积小 50–70%」查无官方出处,入黑名单。
修订后的表反而更干净:
| 项目 | 参照基线 | 官方口径 |
|---|---|---|
| Kotlin/Wasm | — | 官方未发布任何体积数据 |
| Scala.js | Scala.js/JS(十年打磨,死代码消除极致) | 约为 JS 的 2 倍 |
Scala.js 官方原话:
> Code size: "The generated code size of the Wasm backend is currently about twice as big as the JS backend in fullLink mode."
> Performance: "If the performance of your application depends a lot on JavaScript interop, the Wasm output may be (significantly) slower than the JS output. For applications whose performance is dominated by computations inside Scala code, the Wasm output should be significantly faster (geomean 30% lower run time)."
> "Keep in mind that performance work on the Wasm backend is still young, compared to a decade of optimizations in the JS backend."
核心洞见:Wasm 的快慢,很大程度上取决于它需要多频繁地跨出自己的国界(JS interop 边界)。
旁证 · 真实世界的体积分布:
| 分位 | 数值 |
|---|---|
| 后 50% 模块 | 2 KB – 14 KB(多为 Base64、校验和一类微工具) |
| P90 | 桌面 381 KB / 移动 316 KB |
| 最大单模块 | 桌面 234 MB |
5.5 Google Sheets:不是范例,是特例
结论:不宜作为「WasmGC 已普遍可用」之证,应表述为「Google 全栈自控条件下的特例」。
因果链(丙丁二队交叉核实):
1. 自家编译器 J2CL
2. 自家引擎 V8(推测内联 + 去虚化,+40%)
3. 自家优化器 Binaryen(J2CL 专属 pass #6888,2024-09-06 合并,落地为 J2CLItableMerging.cpp + J2CLOpts.cpp)
4. 关键取舍:把正则表达式交还给 Chrome 原生 RegExp 引擎(+100×)——本质是把活儿还给宿主,而非 Wasm 自身变强
初期真相:Google Sheets 迁往 WasmGC 之初,比 JS 慢约 2 倍,后经上述手段方才翻盘。
不对称性事实(这是立论的全部依据,已预堵反驳):
- Binaryen 中唯一面向单一语言的 WasmGC 专属优化,是为 Java(J2CL)而设
- 无 Kotlin 专属 pass、无 Dart、无 Scala.js
- 而 J2CL 恰是唯一拥有旗舰级 WasmGC 生产部署的前端
时间线要害:#6888 早于 3.0 定稿整整一年,且至今仍在树内 → 3.0 并未解决它所绕开的问题。
与 §3.7 的遥测强推断合看,「特例」可升格为「几乎是唯一实例」。
5.6 3.0 特性集仍不足以支撑真实编译器
证据一 · Scala.js 被迫静默忽略 @JSExport
> "Due to the feature set of Wasm 3.0, it is not possible to implement the semantics of @JSExport. Therefore, the Wasm backend currently silently ignores all @JSExport and @JSExportAll annotations."
后果:JS 代码无法调用 Scala 类的导出方法;连 toString() 都不可用,Scala 对象在 JS 侧无法参与字符串拼接。需等 Custom Descriptors 提案(Stage 3,不在 3.0 内)落地方能解决。
证据二 · Scala.js 尚不支持多模块输出,module split style 必须设为 FewestModules。
证据三 · Binaryen J2CL 专属 pass:WasmGC 类型系统表达不了 Java 式接口分发,导致每个 Java 对象被迫多背一个引用字段。
5.7 WASI 0.3:与核心规范恰好相反的失衡
WASI 0.3.0 发布于 2026-06-11(非坊间所传的 2 月),官方原话:
> "WASI 0.3.0 was released on June 11, 2026."
实现状况——官方支持清单只有 Wasmtime 与 jco,且用的是将来时:
> "Wasmtime 45 runs the latest release candidate,Wasmtime 46 will ship WASI 0.3.0 with Component Model Async enabled by default"
「所有主流运行时(Wasmtime/WasmEdge/Wasmer)都支持 P3 RC」系讹传——官方名单里根本没有 WasmEdge 和 Wasmer。
由此形成一组漂亮的对称:
| 方向 | 例证 | |
|---|---|---|
| 核心规范 | 引擎先于标准数年 | WasmGC 在 Chrome 119(2023)发货,标准 2025-09-17 才封版 |
| WASI 0.3 | 标准先于实现 | 2026-06-11 发布,至今无运行时默认支持 |
5.8 3.0 有没有催生新的产品品类?
诚实的回答:没有可查证的、由 Wasm 3.0 独家催生的新品类。
- WasmGC 的价值是让已有品类能用更多语言写,而不是创造新品类
- Memory64 理论上打开了「浏览器内处理 >4GB 数据」的场景(大型 CAD、8K 视频、端侧推理),但未找到任何已上线的、明确依赖它的商业产品——且遥测给出了比「找不到」强得多的证据:每 4 万个 Wasm 页面才 1 个,且还在跌
- 服务端 Wasm(边缘函数、插件沙箱)的品类在 3.0 之前就已存在,3.0 未改变其架构
---
第六章 · 反方论纲:该警惕的
本章由丁队(devils-advocate)主笔,主编校订。先声明本章不主张什么,以免被读成情绪化唱衰:
- 不主张 Wasm 是失败的技术
- 不主张 3.0 的技术设计有误
- 不主张 开发者应回避 Wasm
可证伪条件(若下列情况发生,本章批评即失效):
1. 某个非 Google 系的 WasmGC 生产案例达到同等规模并公开数据
2. V8WasmExnRef 在 12 个月内追平或超过 legacy EH
3. 出现明确依赖 Memory64 的商业产品,且公开性能数据证明其净收益为正
4. Safari 正式版默认开启 Memory64/Relaxed SIMD/多内存(追上 WebKit trunk 的 stable 默认)且官方特性表修正开关名——则「发布延迟/特性表失修」相关批评失效(见 §4.3.3)
6.1 证据环境已被污染
公开互联网上的「Wasm 3.0 性能数据」已被 AI 生成内容严重污染。本研究在核查过程中反复撞见:同一组数字(95% 原生速度、7500 万 req/s、5.5% 采用率)在数十篇文章间循环引用,回溯到底全部找不到一手实测。
其中最典型的一例,是那篇「5.5%」的源头文章——它在同一段内自相矛盾:一句写 "5.5% of Chrome user sessions",紧接的下一句写成 "5.5% of websites now use WebAssembly"。同一个数字,两个不同的口径,作者自己都没察觉。
> 这是本报告坚持「凡数字只采一手源」的直接原因。
6.2 标准复杂度:从 MVP 极简主义滑向「又一个 JVM/CLR」
Wasm MVP 的设计哲学是极简——一个小到可以在几千行内实现完整验证器的指令集。3.0 之后:
- 类型系统引入递归组、同构递归子类型判定、
sub/final显式子类型 - 验证器复杂度大幅上升,新引擎的实现门槛陡增
- 类型系统本身成为新的 CVE 攻击面
6.3 「Wasm 从不删东西」:复杂度单调递增的机理
legacy EH 的案例已在 §2.6 详述。此处提炼机理:
Wasm 的兼容性承诺 + Web 的「不破坏网络」原则 = 任何进入引擎的指令,实际上永远无法移除。
规范作者自己在 issue #281 的 Migration Plan 里写下 "deprecation may not be feasible"。这意味着:
> 每一次「设计上的诚实回退」,代价都是引擎里永久多出一套指令。 3.0 修好了异常处理的设计,但没人能把旧的那套拆掉。
跨宿主场景更糟:这套「旧指令」在浏览器之外根本不成立(丁队×乙队交叉质证)。 三大浏览器零弃用、零移除开关——V8 的 legacy_eh 在「已发布特性」组值为 true,Firefox 检索 wasm_legacy/exnref/try_table 零匹配;但 Wasmtime 已把 legacy EH 默认关闭且配置标 #[deprecated = "...may be removed at any time without warning..."],Wasmer/WasmEdge 根本未实现。同一份用旧 EH 编译的 .wasm 在 Chrome 跑得好好的,丢进 Wasmtime 直接校验失败——「Wasm 3.0 统一了异常处理」在跨宿主场景不成立。wasm-tools 源码注释写得很直白:*"Support this feature as long as all leading browsers also support it"*——工具链等浏览器先删,浏览器无删的动机,新旧并行已超四年。
6.4 未兑现的承诺
| 承诺 | 现状 |
|---|---|
| 「取代 JavaScript」 | 0.0613% 页面加载。JS 毫发无伤 |
| 「取代 Docker」(Solomon Hykes 2019 年名言) | 服务端 Wasm 有真实场景(边缘函数、插件沙箱),但远未取代容器 |
| 「DOM 直接访问」 | 仍无解。所有 DOM 操作仍需经 JS 桥 |
| 「冷启动碾压」 | 相对 Firecracker 有优势,但相对 V8 isolates 优势不明显——而后者正是 Cloudflare Workers 的实际选型 |
6.5 版本叙事本身的可信度问题
这是丁队最有分量的一节,与 §1.2、§1.4、§1.5 互为表里:
1. 「Wasm 3.0」在 W3C 上物理不存在——正文住在短名 wasm-core-2 的文档里
2. 「3.0 特性清单」两份官方文档三处对不上,且脚注编号自证 Profiles 绕过了流程
3. living standard 的透明度黑洞——「Wasm 3.0」指哪一天的哪份文本?无从锁定
4. 无独立勘误表——改了什么,只能自己 diff git
> 合起来:一个对外宣称版本号、并以此组织传播的标准,其版本边界在技术上是模糊的。这不影响规范的技术质量,但确实影响一切基于「3.0」这个标签的判断——包括本报告自己。
---
第七章 · 主编裁定速查表
全部十八条裁定,供快速核对。凡本报告正文与此表冲突者,以此表为准。
| # | 议题 | 裁定 |
|---|---|---|
| 一 | 「Wasm 3.0 成为 W3C 推荐标准」 | ❌ 不严谨。3.0 不是、且按常青模式设计永远不会成为 Recommendation |
| 二 | Memory64 的性能代价 | ✅ 坐实。10%–100% 回退,机理是边界检查无法消除 |
| 二附 | 16 EB 内存 | ⚠️ 六层落差。①规范 16 EiB → ⑥实践摸不到 16 GiB |
| 三 | Wasm 采用率 | ✅ 三真口径(0.0613% / 0.35% / 0.05–0.1%),❌ 一假数字(5.5%) |
| 四 | 「WasmGC 让包更小」 | ❌ 伪命题。Kotlin 数字查无出处;唯一有官方数据者(Scala.js)说大 2 倍 |
| 五 | 3.0 特性集是否够用 | ❌ 不够。Scala.js 被迫静默忽略 @JSExport,连 toString() 都不可用 |
| 六 | 「完整 3.0」门槛 | Chrome 137 / Firefox 134 / Safari 26 / Node 25,比单特性首次支持高 18 个大版本 |
| 七 | Google Sheets 案例 | ⚠️ 降级为「Google 全栈自控条件下的特例」,初期曾比 JS 慢 2 倍 |
| 八 | Memory64 最锋利的一刀 | 「能要,不能还」——Wasm 至今无法把内存归还操作系统(提案联席主席原话) |
| 九 | table64 | ⚠️ 一项「已完成」特性里未完工的零件,V8/Firefox 均标 WIP |
| 十 | Threads 是否在 3.0 | ❌ 不在。Phase 4。并发/控制流领域,3.0 未纳入任何新特性 |
| 十一 | Safari 的 Memory64 问号 | ✅ 已解:实现就绪,未发货(三条 WebKit 一手证据互证) |
| 十二 | 引擎实现限制 | ⚠️ 三家分歧巨大:多内存上限 V8 十万 vs 另两家一百,差 1000 倍,合规测试查不出 |
| 十三 | WASI 0.3 | 2026-06-11 发布,至今无运行时默认支持。与核心规范的错位方向恰好相反 |
| 十四 | 3.0 十项特性野外用量 | 见 §3.2。最高 8.6~8.7%(WasmGC/类型化函数引用/JS String Builtins 三曲线几乎同值,占 Wasm 页面),最低为 0 |
| 十五 | 3.0 到底含哪些特性 | ⚠️ 两份官方文档三处对不上,脚注编号自证 Profiles 绕过流程 |
| 十六 | 异常处理三代史 | 六年绕一圈回到 exnref;野外 99.9965%(中位口径)仍跑 Gen2;规范作者自承旧方案「可能永远删不掉」 |
| 十七 | DET Profile × Relaxed SIMD | ✅ 不矛盾(主编原假设有误,已更正)。是 3.0 最值得称道的设计决策,但落地仅 Wasmtime 一家 |
| 十八 | 3.0 之后的勘误 | ❌ 无独立 errata。常青模式的透明度缺口 |
第八章 · 结论:一次为编译器作者准备的升级
8.1 全报告的核心判断
把五项特性按「铺开了没有」排开,会看到一条极清楚的界线:
| 特性 | 状况 | 它解决的是谁的问题 |
|---|---|---|
| Bulk Memory(2.0) | ✅ 18.7 万模块 | 应用开发者:memory.copy/fill,人人都要的性能刚需 |
| Custom Descriptors(非 3.0,未标准化) | ✅ 八个月从零冲到 0.00506% | 大厂产品:Google 已在生产跑(见 §3.8/§3.9) |
| WasmGC(3.0) | ❌ 平台期,同比 1.09 倍 | 语言实现者:不必自带 GC |
exnref(3.0) | ❌ 每 27 万页 1 个(中位口径) | 语言实现者:异常处理的规范整洁性 |
| Memory64(3.0) | ❌ 比两年前还低 | 极少数:确实需要 >4 GB 的人 |
这个判断还能解释另外两件已查实的事:
1. WASI 0.3 的主题恰恰是异步互操作(stream/future/async func,解决 sandwich problem)——又一个应用侧痛点,而它不在 3.0 核心规范内
2. Scala.js 因 3.0 特性不足而静默忽略 @JSExport——又一个互操作缺口,需等不在 3.0 内的 Custom Descriptors
> 把三件事连起来:生态真正饥渴的是跨越 Wasm 与宿主边界的能力(JSPI、WASI async、JS interop),而 3.0 交付的是 Wasm 内部的语言表达力(GC、异常、大内存)。这解释了为什么「史上最大更新」在用量曲线上几乎看不见。
⚠️ 必须同时呈现的反例与修正:Relaxed SIMD 与尾调用(return_call)同样偏语言实现者侧,曲线却翻了数百倍。但经口径审计,这不是「被广泛采用」的证据,反而是反证——两者都是单一来源上线导致的单日阶跃(Relaxed SIMD 2025-08-07 单日 95×;尾调用 2026-06-30 单日 98×),具体部署方公开数据查不出。一个来源上线就能让曲线翻百倍,恰恰证明盘子之小。故上述判断是一个解释性框架,不是无懈可击的规律。
8.2 给不同读者的判断
给技术决策者
- 不要因为「3.0 发布了」而启动迁移。 3.0 的价值绝大部分流向语言实现者,你多半不是。
- Memory64 除非你确实需要 >4 GB,否则不要碰。 它是负收益特性,在 32 位设备上更是纯亏。
- 若你在选 GC 语言上 Web:Java/J2CL 有旗舰案例但依赖 Google 全栈;Scala.js 稳定但体积翻倍;Kotlin/Wasm 仍是 Beta 且无任何官方量化数据。
- 服务端场景另当别论——本报告的用量数据完全看不见服务端,那里的图景可能截然不同。
- 写跨引擎代码时,按 SpiderMonkey/JSC 的限制设计,不要按 V8 的余量。 多内存差 1000 倍,且合规测试查不出来。
- 异常处理:野外 99.9965%(中位口径)仍是 legacy。 若你的工具链默认产出 legacy EH,这不是落后,这是现状。
- 「支持 WasmGC」与「支持完整 3.0」相差 18 个大版本。 定 baseline 时用后者。
- Wasm 的快慢,主要取决于你跨越 JS 边界的频率,不取决于 Wasm 本身。
- 常青模式解决了流程问题,但制造了叙事问题。 没有冻结版本、没有勘误表、特性清单两份文档对不上——「3.0」这个标签的信息量比它看上去要低。
- 引擎已经跑在标准前面(Component Model、Custom Page Sizes 都已在 SpiderMonkey 源码里)。真正决定野外用什么的,不是标准化流程,而是有没有一家大厂在自家产品里用它——Custom Descriptors 未标准化,用量却是已标准化 Memory64 的 3466 倍。
8.3 最后一句
Wasm 3.0 的技术是诚实的:类型系统的重构该做,异常处理的回退该认,profile 的分层做得漂亮。
但它的叙事欠了债。 一个在 W3C 上不存在独立文档、特性清单自相矛盾、旗舰特性野外用量为万分之零点二四的版本,被包装成「史上最大更新」并由二手传播放大成「5.5% 的网页都在用」——这中间隔着的九十倍,不是标准委员会造成的,但也不是没有关系。
真正值得关心的问题不是「Wasm 3.0 有多强」,而是:
> 下一次更新,会不会终于为应用开发者做点什么?
---
附录 A · 本报告的自我纠错清单
本研究过程中,主编共有 四处判断被队员用一手证据推翻,全部记录在此,以示证据链的可检验性:
| # | 主编原判断 | 推翻方 | 实况 |
|---|---|---|---|
| 1 | V8 注释「TODO(clemensb)... For now, use 16GB」自承临时 | 乙队 | 该注释已从当前 trunk 删除,替换为引用 js-api 规范 |
| 2 | SpiderMonkey 有 MaxMemory64LimitField = 1<<48 | 乙队 | 当前源码中不存在此常量,出自二手汇总文的旧版快照 |
| 3 | Kotlin「体积小 50–70%」与 Scala.js「大 2 倍」两者皆真 | 丙队 | Kotlin 数字查无官方出处,kotlinlang.org 全页无体积数字 |
| 4 | 「Almanac × Chrome 遥测」双源互证 3.0 特性近零 | 丙队 | Almanac 根本没测这些特性,沉默的源不构成一票 |
| 5 | DET Profile 与 Relaxed SIMD 自相矛盾 | 甲队查规范原文 | 二者不矛盾,DET 把 relaxed 指令钉死为固定语义。是我问错了 |
附录 B · 不可引用数字黑名单
见 §0.3。核心五条:5.5%/4.5%/41% 采用率、16+EB 可用内存、Kotlin 体积小 50–70%、Coremark 95% 原生速度、Safari 145。
附录 C · 研究方法与分工
| 分队 | 职责 | 卷宗 |
|---|---|---|
| 甲队 spec-scholar | 规范考据:逐条核对规范正文、提案流水线、SpecTec、类型系统 | 85 KB |
| 乙队 engine-tracker | 引擎实测:V8/SpiderMonkey/JSC/Wasmtime 源码常量、支持矩阵、性能基准 | 42 KB |
| 丙队 ecosystem-scout | 生态侦察:语言工具链、Web Almanac 全网抽样、生产案例 | 65 KB |
| 丁队 devils-advocate | 批判前瞻:反方论纲、污染数据识别、可证伪条件 | 39 KB |
| 主编 team-lead | 交叉裁定 18 条 + Chrome 官方遥测独家采集(13 条完整时序) | 44 KB + 11 KB |
- W3C/WebAssembly 核心规范 Change History、
profiles.html、js-api#limits WebAssembly/proposals—finished-proposals.md、各提案 OverviewWebAssembly/spec— issue #281(异常处理)、issue #1892(内存上限)- V8 源码
src/wasm/wasm-limits.h;SpiderMonkeyWasmConstants.h;JSCWasmLimits.h - Wasmtime 源码(Collector 枚举);Binaryen PR #6888
- SpiderMonkey 官方博客(Memory64 性能,2025-01-15)
- Scala.js 官方 WebAssembly 文档;kotlinlang.org Wasm 概览
- dotnet/runtime issue #94420
- Chrome Platform Status 数据 API(13 个 bucket 完整时序)
- HTTP Archive / Web Almanac 2025 WebAssembly 章
- WebKit Bugzilla 300538、trunk commit
0d0080ea - wasi.dev/releases/wasi-p3
> 报告完成于 2026-08-06。 > 数据会变,结论未必长青。若读者在未来核对本报告任一数字,请以文中标注的抓取日为准,并按附录 B 的方法自行复现。