言語がGCを捨てる日。WebAssembly GCが暴く、二重管理の不条理

長らく「C/C++やRustのための特権」であったWebAssembly。しかしWasm GCの標準化により、Java、Kotlin、Dartといったガベージコレクション依存の言語たちが、自前の重い清掃員(GC)を捨ててブラウザのエンジンへすり寄ろうとしています。

透明なマイクロチップ内で、メモリの断片が美しく回収・最適化されていく近未来の光景

ブラウザという空間の中で、これまで密かに行われていた「二重管理の不条理」をご存知でしょうか。 ブラウザのJavaScriptエンジン(V8など)には、すでに極めて優秀で高速なガベージコレクタ(GC)が常駐し、メモリの清掃作業を完璧にこなしています。それにもかかわらず、Wasm GC登場以前のWebAssembly(Wasm)へコンパイルされたガベージコレクション依存の言語たち(Java、C#、Goなど)は、わざわざ自分たち専用の「自前GC」をバイナリに同梱し、線形メモリという閉鎖空間の中でせっせと清掃活動を行っていました。

出張先のホテルに優秀な清掃スタッフがいるのに、わざわざ自宅から専属の清掃員を飛行機に乗せて連れて行くような滑稽な振る舞いです。Wasm GC(Garbage Collection)の標準化は、この不条理を暴き、言語たちから「自前GC」という重荷を剥ぎ取るための決定的なプロトコルです。

Wasm GC: メモリ管理の委譲

ブラウザ / Wasm Runtime

ホストが直接回収

Wasm GCの導入で移行

従来: 線形メモリ+自前GCの不条理

自力で巡回・回収

Wasm Linear Memory (フラットなバイト配列)

自前のGCランタイム (バイナリに同梱)

言語のオブジェクト

ホストエンジンのGC (V8 GC等)

Wasm GC Types (struct, array)

今回は、このメモリの深淵を渡ろうとする主要言語たちの現在地と、その裏側にある思惑を観察してみましょう。

各言語が直面する現実と捨てる者・悩む者・動かない者#

Wasmの命令セットに構造体や配列といった高水準な型を直接定義し、その管理をホスト側へ委譲する。この仕組みに対する各言語コミュニティの反応は、綺麗に分かれています。

言語エコシステム現在試せる経路主な制約と現状の壁
Dart (Flutter)Stable (with Preview)最も野心的。ブラウザ互換性の問題(Firefox/Safariの一部非対応)やiOSの制約などがあり、現状は完全なプロダクションではなく「JSフォールバック前提の先行投入」という立ち位置。
Kotlin (Kotlin/Wasm)Beta / RCJetBrainsが総力を挙げて推進中。JSへの依存を断ち切る強力な選択肢だが、依然としてブラウザ間のGC挙動の差異に影響を受ける。
Java (TeaVM)Early AccessTeaVMによる実験的な対応。長年築かれた巨大なJavaエコシステム(JNIやリフレクション)とWasm GCの静的型の相性は悪く、溝は深い。
Scala (Scala.js)ExperimentalScala.jsバックエンドとして実験的サポート。ただし、Wasm出力のコードサイズが既存のJS出力の約2倍に膨らむという逆風も観測されている。
OCamlEarly Draft関数型言語特有の表現力をWasm GC構造体にどうマッピングするか、基礎研究の段階。
GoIssue Open (未定)Go特有のメモリレイアウトやgoroutineの恩恵をホストGCへ委ねることが難しく、Wasm GCへの移行には慎重。自前GC持ち込み型を維持。

すべての言語がWasm GCの恩恵を受けられるわけではありません。Google(Dart)やJetBrains(Kotlin)が自らのUIフレームワークをブラウザでネイティブに動かすために躍起になっている一方で、Goのように「自らのメモリモデルを手放すことの代償」を嫌う言語もあります。

また、Wasm GCにすれば無条件にバイナリサイズが数十キロバイトに縮むというのは幻想です。Chromeの検証結果として挙がる「数キロバイトへ激減したJavaのサンプル」のような成功例がある一方で、Scala.jsのように現時点ではJavaScriptへのトランスパイル出力よりサイズが肥大化してしまうケースも存在します。GCランタイムを捨てることはできても、言語特有の型のセマンティクスをWasm GCの厳格な構造体へマッピングするためのオーバーヘッドが新たな重荷になっているのです。

WASI・Component Modelとの直交性とすれ違い#

以前の記事(WebAssembly Component Modelの野望)で扱ったWASIやComponent Modelと、今回のWasm GCの進展は、どのように交差するのでしょうか。

これらは別の軸で進んでいる独立した仕様です。 例えば、Dartはブラウザ上の描画パフォーマンスに特化してWasm GCを利用していますが、ブラウザ外でのファイルアクセス(WASI)を積極的に狙っているわけではありません。逆に、Kotlin/Wasmは wasm-wasi ターゲットを用意し、Wasm GCを利用しつつブラウザ外のWasmtime等のランタイムで動かす野心も見せています(ただしComponent Modelサポートは一部EAP限定)。

バックエンドのランタイム(Wasmtime 27.0等)でもWasm GCのサポートが始まりましたが、Wasm GCのオブジェクトをComponent Modelの境界を越えて別の言語に安全に渡すための仕様(GC to Component Model lowering/lifting)は、まだ議論と実装の過渡期にあります。「ブラウザ上の単一モジュール」としてのWasm GCは安定期に入りましたが、モジュール同士が絡み合うバックエンドのエコシステムは、まだピースが足りていないのです。

静的型の箱と、はみ出す言語たち#

ガベージコレクションという最も面倒な仕事をホストに押し付け、身軽になったDartやKotlinがJavaScriptの領土をどれほど侵食していくのか。 忘れてはならないのは、Wasm GCの構造体は「静的型付け」を強く前提としている点です。つまり、RubyやPythonといった動的型付け言語をこの綺麗な箱の中に収めるのは極めて困難です。彼らは相変わらず巨大なインタプリタと自前GCを線形メモリに抱え込み、重苦しい足取りでブラウザの端を歩き続けるしかありません。

技術の進化は、常に誰かを置き去りにします。皆様が日々の業務で無自覚に叩いているその言語のコンパイラが、水面下でWasmの厳格な型システムとどれほど血みどろの格闘を繰り広げているか。たまには、ブラウザのデベロッパーツールを開いて、そこから吐き出される数十キロバイト(あるいは数メガバイト)の美しいバイトコードの塊に、同情の目を向けてみるのも悪くありませんよ。