WebAssembly Garbage Collection (WasmGC) ist jetzt in Chrome standardmäßig aktiviert

Es gibt zwei Arten von Programmiersprachen: automatische Speicherbereinigungen und Programmiersprachen, die eine manuelle Speicherverwaltung erfordern. Beispiele dafür sind Kotlin, PHP und Java. Beispiele für Letzteres sind C, C++ oder Rust. In der Regel ist bei höheren Programmiersprachen die Wahrscheinlichkeit der automatischen Speicherbereinigung als Standardfunktion höher. In diesem Blog-Post liegt der Fokus auf solchen durch automatische Speicherbereinigung bereitgestellten Programmiersprachen und wie sie zu WebAssembly (Wasm) kompiliert werden können. Aber womit beginnt die automatische Speicherbereinigung (GC)?

Unterstützte Browser

  • Chrome: 119. <ph type="x-smartling-placeholder">
  • Edge: 119. <ph type="x-smartling-placeholder">
  • Firefox: 120 <ph type="x-smartling-placeholder">
  • Safari: wird nicht unterstützt. <ph type="x-smartling-placeholder">

Automatische Speicherbereinigung

Vereinfacht ausgedrückt, ist das Konzept der automatischen Speicherbereinigung der Versuch, Arbeitsspeicher freizugeben, der vom Programm zugewiesen wurde, aber nicht mehr referenziert wird. Dieser Speicher wird als Garbage bezeichnet. Es gibt viele Strategien für die Implementierung der automatischen Speicherbereinigung. Eine davon ist die Referenzzählung. Dabei wird die Anzahl der Verweise auf Objekte im Speicher gezählt. Wenn es keine weiteren Verweise auf ein Objekt gibt, kann es als nicht mehr verwendet markiert werden und somit für die automatische Speicherbereinigung bereit sein. Die automatische Speicherbereinigung von PHP verwendet die Referenzzählung und die Verwendung der Funktion xdebug_debug_zval() der Xdebug-Erweiterung ermöglicht Ihnen, einen Blick hinter die Kulissen zu werfen. Sehen Sie sich das folgende PHP-Programm an.

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

Das Programm weist einer neuen Variablen namens a eine Zufallszahl zu, die in einen String umgewandelt wird. Anschließend werden die beiden neuen Variablen b und c erstellt und ihnen der Wert a zugewiesen. Danach wird „b“ der Nummer 42 neu zugewiesen und c wird dann aufgehoben. Schließlich wird der Wert von a auf null gesetzt. Wenn Sie jeden Schritt des Programms mit xdebug_debug_zval() annotieren, sehen Sie, wie der Referenzzähler der automatischen Speicherbereinigung in Betrieb ist.

<?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');
?>

Im obigen Beispiel werden die folgenden Logs ausgegeben, in denen Sie sehen, wie die Anzahl der Verweise auf den Wert der Variablen a nach jedem Schritt abnimmt. Dies ist angesichts der Codesequenz sinnvoll. (Ihre Zufallszahl ist selbstverständlich anders.)

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

Es gibt noch weitere Herausforderungen bei der automatischen Speicherbereinigung, wie das Erkennen von Zyklen. Für diesen Artikel reicht jedoch ein grundlegendes Verständnis der Referenzzählung aus.

Programmiersprachen werden in anderen Programmiersprachen implementiert.

Es mag sich wie eine Einführung anfühlen, aber Programmiersprachen werden in anderen Programmiersprachen implementiert. Die PHP-Laufzeit ist beispielsweise hauptsächlich in C implementiert. Sie können sich den PHP-Quellcode auf GitHub ansehen. Der Code für die automatische Speicherbereinigung von PHP befindet sich hauptsächlich in der Datei zend_gc.c. Die meisten Entwickler installieren PHP über den Paketmanager ihres Betriebssystems. Entwickler können aber auch PHP aus dem Quellcode erstellen. In einer Linux-Umgebung wird mit den Schritten ./buildconf && ./configure && make beispielsweise PHP für die Linux-Laufzeit erstellt. Das bedeutet aber auch, dass die PHP-Laufzeit für andere Laufzeiten wie Wasm kompiliert werden kann.

Herkömmliche Methoden der Portierung von Sprachen in die Wasm-Laufzeit

PHP-Skripts werden unabhängig von der Plattform, auf der PHP ausgeführt wird, in denselben Bytecode kompiliert und von der Zend Engine ausgeführt. Zend Engine ist eine Compiler- und Laufzeitumgebung für die PHP-Skriptsprache. Sie besteht aus der Zend Virtual Machine (VM), die sich aus dem Zend Compiler und dem Zend Executor zusammensetzt. Sprachen wie PHP, die in anderen übergeordneten Programmiersprachen wie C implementiert sind, weisen in der Regel Optimierungen auf, die auf bestimmte Architekturen wie Intel oder ARM ausgerichtet sind, und erfordern für jede Architektur ein anderes Back-End. In diesem Kontext steht Wasm für eine neue Architektur. Wenn die VM über architekturspezifischen Code wie Just-in-Time (JIT) oder Vorab-Kompilierung (AOT) verfügt, implementiert der Entwickler für die neue Architektur auch ein Back-End für JIT/AOT. Dieser Ansatz ist sehr sinnvoll, da oft der Hauptteil der Codebasis für jede neue Architektur einfach neu kompiliert werden kann.

Angesichts des Low-Level-Wasm ist es natürlich, den gleichen Ansatz dort auszuprobieren: den Haupt-VM-Code mit dem Parser, der Bibliotheksunterstützung, der automatischen Speicherbereinigung und dem Optimierungstool für Wasm neu zu kompilieren und bei Bedarf ein JIT- oder AOT-Backend für Wasm zu implementieren. Dies ist seit dem Wasm-MVP möglich und funktioniert in der Praxis in vielen Fällen gut. Der WordPress Playground basiert sogar auf in Wasm kompilierten PHP. Weitere Informationen zum Projekt finden Sie im Artikel Build In-Browser WordPress Experiences mit WordPress Playground und WebAssembly.

PHP Wasm wird im Browser jedoch im Kontext der Hostsprache JavaScript ausgeführt. In Chrome werden JavaScript und Wasm in V8 ausgeführt, der Open-Source-JavaScript-Engine von Google, die ECMAScript wie in ECMA-262 angegeben implementiert. Und V8 hat bereits eine automatische Speicherbereinigung. Das bedeutet, dass Entwickler, die z. B. in Wasm kompilierte PHP verwenden, am Ende eine Garbage Collector-Implementierung der portierten Sprache (PHP) an den Browser senden, der bereits über einen Garbage Collector verfügt, was so viel Aufwand verursacht, wie es klingt. Hier kommt WasmGC ins Spiel.

Das andere Problem des alten Ansatzes, bei dem Wasm-Module auf dem linearen Speicher von Wasm eine eigene Speicherbereinigung aufbauen, besteht darin, dass es dann keine Interaktion zwischen Wasms eigenem Garbage Collector und dem integrierten Garbage Collector der kompilierten Wasm-Sprache gibt, was zu Problemen wie Speicherlecks und ineffizienten Datenerfassungsversuchen führt. Wenn Sie Wasm-Modulen die Wiederverwendung der vorhandenen integrierten GC erlauben, können diese Probleme vermieden werden.

Programmiersprachen mit WasmGC in neue Laufzeiten übertragen

WasmGC ist ein Vorschlag der WebAssembly Community Group. Die aktuelle Wasm-MVP-Implementierung kann nur Zahlen, d. h. Ganzzahlen und Gleitkommazahlen, im linearen Arbeitsspeicher verarbeiten. Da das Angebot für Referenztypen versendet wird, kann Wasm zusätzlich externe Referenzen beibehalten. WasmGC fügt jetzt Struktur- und Array-Heap-Typen hinzu, was bedeutet, dass die nicht lineare Speicherzuweisung unterstützt wird. Jedes WasmGC-Objekt hat einen festen Typ und eine feste Struktur. Dadurch können VMs auf einfache Weise effizienten Code für den Zugriff auf ihre Felder generieren, ohne das Risiko von Minderungen wie bei dynamischen Sprachen wie JavaScript zu riskieren. Dadurch wird WebAssembly über Struktur- und Array-Heap-Typen effizient verwaltete Sprachen auf hoher Ebene unterstützt, mit denen Sprach-Compiler, die auf Wasm abzielen, in einen Garbage Collector in der Host-VM integrieren können. Vereinfacht ausgedrückt bedeutet dies, dass bei der Portierung einer Programmiersprache zu Wasm bei WasmGC die automatische Speicherbereinigung der Programmiersprache nicht mehr Teil des Ports sein muss, sondern die vorhandene automatische Speicherbereinigung verwendet werden kann.

Um die tatsächlichen Auswirkungen dieser Verbesserung zu überprüfen, hat das Wasm-Team von Chrome Versionen des Fannkuch-Benchmarks für C, Rust und Java zusammengestellt. Dabei werden Datenstrukturen während der Bearbeitung zugewiesen. Die Binärdateien für C und Rust können je nach Compiler-Flags zwischen 6.1 K und 9.6 K liegen. Die Java-Version ist mit nur 2.3 K viel kleiner. C und Rust enthalten keine automatische Speicherbereinigung, bündeln aber trotzdem malloc/free zur Speicherverwaltung. Java ist hier kleiner, weil es überhaupt keinen Speicherverwaltungscode bündeln muss. Dies ist nur ein konkretes Beispiel, aber es zeigt, dass WasmGC-Binärdateien das Potenzial haben, sehr klein zu sein, und dies ist noch bevor eine erhebliche Optimierung im Hinblick auf die Größe erfolgt.

Eine von WasmGC portierte Programmiersprache in Aktion sehen

Kotlin-Wasm

Eine der ersten Programmiersprachen, die dank WasmGC in Wasm portiert wurde, ist Kotlin in Form von Kotlin/Wasm. Die Demo mit dem Quellcode, die vom Kotlin-Team zur Verfügung gestellt wurde, wird in der folgenden Liste angezeigt.

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"

Du fragst dich vielleicht, worum es geht, denn der obige Kotlin-Code besteht im Grunde aus den JavaScript OM APIs, die in Kotlin konvertiert wurden. In Kombination mit Compose Multiplatform wird dies nach und nach sinnvoller. So können Entwickler auf der Benutzeroberfläche aufbauen, die sie möglicherweise bereits für ihre Android Kotlin-Apps erstellt haben. Mit der Kotlin-/Wasm-Bildanzeigen-Demo und dem Quellcode können Sie sich frühzeitig damit beschäftigen. Das Kotlin-Team stellt Ihnen dies zur Verfügung.

Dart und Flutter

Die Teams von Dart und Flutter bei Google bereiten ebenfalls den Support für WasmGC vor. Die Dart-to-Wasm-Kompilierung ist fast abgeschlossen und das Team arbeitet an der Unterstützung von Tools für die Bereitstellung von Flutter-Webanwendungen, die für WebAssembly kompiliert wurden. Informationen zum aktuellen Stand der Arbeit finden Sie in der Flutter-Dokumentation. Die folgende Demo ist die Flutter-WasmGC-Vorschau.

Weitere Informationen zu WasmGC

Dieser Blogpost hat kaum an der Oberfläche gekratzt und bietet größtenteils einen allgemeinen Überblick über WasmGC. Weitere Informationen zu dieser Funktion finden Sie unter den folgenden Links:

Danksagungen

Hero-Image von Gary Chan auf Unsplash. Dieser Artikel wurde von Matthias Liedtke, Adam Klein, Joshua Bell, Alon Zakai, Jakob Kummerow, Clemens Backes, Emanuel Ziegler und Rachel Andrew gelesen.