Chrome 現已預設啟用 WebAssembly 垃圾收集 (WasmGC)

程式設計語言有兩種類型:垃圾收集的程式設計語言,以及需要手動管理記憶體的程式設計語言。前者的範例包括 Kotlin、PHP 或 Java。後者範例為 C、C++ 或 Rust。一般而言,較高層級的程式設計語言較有可能將垃圾收集當做標準功能。這篇網誌文章的重點在於垃圾收集的程式設計語言,以及如何將這些程式編譯為 WebAssembly (Wasm)。但垃圾收集 (通常稱為 GC) 的第一步是什麼?

瀏覽器支援

  • 119
  • 119
  • 120
  • x

垃圾收集

簡而言之,垃圾收集的概念是嘗試收回程式分配,但已不再參照的記憶體。這類記憶體稱為垃圾。實作垃圾收集的策略有很多。其中一個是「參照計數」,目標是計算記憶體中物件的參照數量。當物件不再參照時,可標示為不再使用,因此可進行垃圾收集。PHP 的垃圾收集器會使用計算計數,而使用 Xdebug 擴充功能的 xdebug_debug_zval() 函式可讓您一探究竟。請考慮使用以下 PHP 程式。

<?php
  $a= (string) rand();
  $c = $b = $a;
  $b = 42;
  unset($c);
  $a = null;
?>

程式會為名為 a 的新變數指派一個隨機數字,該數字會投放至新變數。接著建立兩個新變數 bc,並為這些變數指派 a 的值,之後,程式碼會將 b 重新指派給號碼 42,然後取消 c。最後,它會將 a 的值設為 null。使用 xdebug_debug_zval() 為程式的每個步驟加上註解,您可以查看垃圾收集器在運作時的參考計數器。

<?php
  $a= (string) rand();
  $c = $b = $a;
  xdebug_debug_zval('a');
  $b = 42;
  xdebug_debug_zval('a');
  unset($c);
  xdebug_debug_zval('a');
  $a = null;
  xdebug_debug_zval('a');
?>

以上範例將輸出下列記錄,您會看到每個步驟後,變數 a 值的參照數量減少了什麼情形 (如程式碼序列很合理)。(隨機號碼當然不同)。

a:
(refcount=3, is_ref=0)string '419796578' (length=9)
a:
(refcount=2, is_ref=0)string '419796578' (length=9)
a:
(refcount=1, is_ref=0)string '419796578' (length=9)
a:
(refcount=0, is_ref=0)null

還有關於垃圾收集的挑戰,例如偵測週期,但是就本文章來說,對參考計數計數有基本瞭解已經足夠。

程式設計語言在其他語言中導入

雖然程式設計語言中都很自在,但程式設計語言也導入了其他程式設計語言,例如,PHP 執行階段主要在 C 中實作。您可以查看 GitHub 上的 PHP 原始碼。PHP 的垃圾收集程式碼主要位於 zend_gc.c 檔案。大部分的開發人員會透過作業系統的套件管理員安裝 PHP。不過,開發人員也可以使用原始碼建構 PHP。舉例來說,在 Linux 環境中,./buildconf && ./configure && make 步驟會為 Linux 執行階段建構 PHP。但這也表示 PHP 執行階段可以編譯至其他執行階段,就像您猜測的 Wasm 一樣。

將語言移植至 Wasm 執行階段的傳統方法

PHP 指令碼與執行 PHP 的平台是獨立運作的,PHP 指令碼會編譯成相同的位元碼,並由 Zend Engine 執行。Zend Engine 是 PHP 指令碼語言的編譯器和執行階段環境。這套系統包含 Zend 虛擬機器 (VM),是由 Zend Compiler 和 Zend Executor 所組成。以其他高階語言 (例如 C) 實作的 PHP 等語言,通常會針對特定架構 (例如 Intel 或 ARM) 執行最佳化作業,而且每種架構都需要不同的後端。在這種情況下,Wasm 代表一個新架構。如果 VM 有特定架構的程式碼,例如及時 (JIT) 或預先 (AOT) 編譯,則開發人員也會為新架構實作 JIT/AOT 的後端。這種方法非常合理,因為通常只要針對每個新架構重新編譯程式碼集的主要部分即可。

由於 Wasm 相當低階,因此嘗試相同的做法很自然:使用其剖析器重新編譯主要 VM 程式碼、使用程式庫支援、垃圾收集和最佳化器,然後視需要為 Wasm 實作 JIT 或 AOT 後端。自 Wasm MVP 推出以來,這已是實現目標,在許多情況下都能順利運作。事實上,PHP 編譯至 WasmWordPress Playground 的技術基礎。如要進一步瞭解專案,請參閱「使用 WordPress Playground 和 WebAssembly 打造瀏覽器中的 WordPress 體驗」一文。

但是,PHP Wasm 會在瀏覽器中執行主機語言 JavaScript 環境。在 Chrome 中,JavaScript 和 Wasm 是以 V8 執行。這是 Google 的開放原始碼 JavaScript 引擎,可導入 ECMA-262 中指定的 ECMAScript。此外,V8 已有垃圾收集器。這意味著開發人員如果運用 PHP 編譯至 Wasm,最後向已經安裝垃圾收集器的瀏覽器發送了一項垃圾收集器實作,但這幾乎是垃圾收集器。這時 WasmGC 就能派上用場。

讓 Wasm 模組在 Wasm 模組之上建構自己的 GC 的另一個問題是,Wasm 本身的垃圾收集器和已編譯至 Wasm 語言的內建垃圾收集器之間沒有互動,這往往會造成記憶體流失以及收集效率低落等問題。允許 Wasm 模組重複使用現有的內建 GC 以避免這些問題。

使用 WasmGC 將程式設計語言移植至新的執行階段

WasmGC 是 WebAssembly 社群群組提案。Wasm MVP 實作目前只能處理數字 (亦即整數和浮點值、線性記憶體),以及出貨的參照類型提案,Wasm 也可以額外保留外部參照。WasmGC 現已新增結構和陣列堆積類型,這表示支援非線性記憶體配置。每個 WasmGC 物件都有固定的類型和結構,可讓 VM 輕鬆產生高效率的程式碼來存取其欄位,而不會對 JavaScript 等動態語言採取最佳化的風險。本提案透過 struct 和陣列堆積類型,讓 WebAssembly 可以有效率地支援高階管理語言,透過 struct 和陣列堆積類型,讓指定 Wasm 的語言編譯器能與主機 VM 中的垃圾收集器整合。簡單來說,這表示使用 WasmGC 將程式設計語言移植至 Wasm 意味著,程式設計語言的垃圾收集器不再需要納入通訊埠,但可使用現有的垃圾收集器。

為了驗證這項改善的實際影響,Chrome 的 Wasm 團隊已編譯 Fannkuch 基準的版本 (會在運作後分配資料結構),這些版本會從 CRustJava 執行。C 和 Rust 二進位檔可能會因為各種編譯器旗標而介於 6.1 K9.6 K 之間,而 Java 版本則小很多,只有 2.3 K!C 和 Rust 不包含垃圾收集器,但我們仍封裝 malloc/free 來管理記憶體,而 Java 較小的原因在於,它完全不需要封裝任何記憶體管理程式碼。這只是一個具體的例子,但可以看出 WasmGC 二進位檔很小,而且在這之前在針對大小最佳化方面發生了重大工作前更是如此。

瞭解 WasmGC 移植的程式設計語言實際應用

Kotlin Wasm

WasmGC 成為 Wasm 技術的第一種程式設計語言之一,是 Kotlin,且格式為 Kotlin/Wasm示範原始碼由 Kotlin 團隊提供,如下所示。

import kotlinx.browser.document
import kotlinx.dom.appendText
import org.w3c.dom.HTMLDivElement

fun main() {
    (document.getElementById("warning") as HTMLDivElement).style.display = "none"
    document.body?.appendText("Hello, ${greet()}!")
}

fun greet() = "world"

現在,您可能會好奇,因為上述的 Kotlin 程式碼基本上是由轉換為 Kotlin 的 JavaScript OM API 組成。這個 API 與 Compose Multiplatform 相結合,可讓開發人員根據先前為 Android Kotlin 應用程式建立的 UI 建構 UI。透過 Kotlin/Wasm 圖片檢視器示範預覽此元素,並探索其原始碼,同樣地,也是由 Kotlin 團隊提供。

Dart 和 Flutter

Google 的 Dart 和 Flutter 團隊也正在準備支援 WasmGC。Dart-to-Wasm 編譯工作幾乎已經完成,我們的團隊也在努力提供工具支援,以便提供編譯至 WebAssembly 的 Flutter 網頁應用程式。如要瞭解工作目前的狀態,請參閱 Flutter 說明文件。以下是 Flutter WasmGC 預先發布版

進一步瞭解 WasmGC

這篇網誌文章幾乎沒有毛刺的表面,並提供 WasmGC 的概要總覽。如要進一步瞭解這項功能,請參閱下列連結:

特別銘謝

主頁橫幅由 Gary Chan 製作,在 Unsplash 網站上。本文評論者:Matthias LiedtkeAdam KleinJoshua BellAlon ZakaiJakob KummerowClemens BackesEmanuel ZieglerRachel}{achel}