Pengumpulan Sampah WebAssembly (WasmGC) kini diaktifkan secara default di Chrome

Ada dua jenis bahasa pemrograman: bahasa pemrograman yang dikumpulkan sampah dan bahasa pemrograman yang memerlukan manajemen memori manual. Beberapa contohnya, antara lain, Kotlin, PHP, atau Java. Contoh yang terakhir adalah C, C++, atau Rust. Sebagai aturan umum, bahasa pemrograman tingkat tinggi cenderung memiliki pembersihan sampah memori sebagai fitur standar. Dalam postingan blog ini, fokusnya adalah pada bahasa pemrograman yang mengumpulkan sampah tersebut dan bagaimana bahasa tersebut dapat dikompilasi ke WebAssembly (Wasm). Tapi, apa itu pembersihan sampah memori (sering disebut GC)?

Dukungan Browser

  • Chrome: 119.
  • Edge: 119.
  • Firefox: 120.
  • Safari: tidak didukung.

Pembersihan sampah memori

Singkatnya, pembersihan sampah memori adalah upaya untuk mendapatkan kembali memori yang dialokasikan oleh program, tetapi tidak lagi direferensikan. Memori seperti itu disebut sampah. Ada banyak strategi untuk mengimplementasikan pembersihan sampah memori. Salah satunya adalah penghitungan referensi yang tujuannya adalah menghitung jumlah referensi ke objek dalam memori. Jika tidak ada lagi referensi ke sebuah objek, objek tersebut dapat ditandai sebagai tidak lagi digunakan dan siap untuk pembersihan sampah memori. Pembersih sampah memori PHP menggunakan penghitungan referensi, dan penggunaan fungsi xdebug_debug_zval() dari ekstensi Xdebug memungkinkan Anda melihat di balik layar. Pertimbangkan program PHP berikut.

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

Program ini menetapkan angka acak yang ditransmisikan ke string ke variabel baru yang disebut a. Kode ini kemudian membuat dua variabel baru, b dan c, lalu menetapkan nilai a kepada kedua variabel tersebut. Setelah itu, kebijakan menetapkan ulang b ke nomor 42, lalu membatalkan penetapan c. Terakhir, kode ini menetapkan nilai a ke null. Dengan menganotasi setiap langkah program dengan xdebug_debug_zval(), Anda dapat melihat penghitung referensi pembersih sampah memori berfungsi.

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

Contoh di atas akan menghasilkan log berikut, tempat Anda melihat penurunan jumlah referensi untuk nilai variabel a setelah setiap langkah, yang wajar mengingat urutan kode. (Angka acak Anda tentunya akan berbeda.)

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

Ada tantangan lain terkait pembersihan sampah memori, seperti mendeteksi siklus, tetapi untuk artikel ini, memiliki tingkat pemahaman dasar tentang penghitungan referensi sudah cukup.

Bahasa pemrograman diimplementasikan dalam bahasa pemrograman lain

Ini mungkin terasa seperti awal, tetapi bahasa pemrograman diimplementasikan dalam bahasa pemrograman lain. Misalnya, runtime PHP terutama diimplementasikan di C. Anda dapat melihat kode sumber PHP di GitHub. Kode pembersihan sampah memori PHP sebagian besar terletak di file zend_gc.c. Sebagian besar pengembang akan menginstal PHP melalui pengelola paket dari sistem operasi mereka. Namun, developer juga dapat membangun PHP dari kode sumber. Misalnya, di lingkungan Linux, langkah-langkah ./buildconf && ./configure && make akan membangun PHP untuk runtime Linux. Namun, ini juga berarti bahwa runtime PHP dapat dikompilasi untuk runtime lain, seperti, seperti dugaan Anda, Wasm.

Metode tradisional untuk melakukan porting bahasa ke runtime Wasm

Secara independen dari platform yang dijalankan PHP, skrip PHP dikompilasi ke dalam bytecode yang sama dan dijalankan oleh Zend Engine. Zend Engine adalah lingkungan compiler dan runtime untuk bahasa skrip PHP. Solusi ini terdiri dari Zend Virtual Machine (VM), yang terdiri dari Zend Compiler dan Zend Executor. Bahasa seperti PHP yang diimplementasikan dalam bahasa tingkat tinggi lainnya seperti C biasanya memiliki pengoptimalan yang menargetkan arsitektur tertentu, seperti Intel atau ARM, dan memerlukan backend yang berbeda untuk setiap arsitektur. Dalam konteks ini, Wasm mewakili arsitektur baru. Jika VM memiliki kode khusus arsitektur, seperti kompilasi tepat waktu (JIT) atau ahead-of-time (AOT), maka developer juga mengimplementasikan backend untuk JIT/AOT bagi arsitektur baru tersebut. Pendekatan ini masuk akal karena sering kali bagian utama codebase dapat dikompilasi ulang untuk setiap arsitektur baru.

Mengingat Wasm level rendahnya, wajar untuk mencoba pendekatan yang sama di sana: Kompilasi ulang kode VM utama dengan parser, dukungan library, pembersihan sampah memori, dan pengoptimalnya ke Wasm, dan implementasikan backend JIT atau AOT untuk Wasm jika diperlukan. Hal ini dapat dilakukan sejak MVP Wasm, dan dalam banyak kasus praktik ini berfungsi dengan baik. Bahkan, PHP yang dikompilasi ke Wasm adalah daya tarik WordPress Playground. Pelajari project ini lebih lanjut dalam artikel Membangun pengalaman WordPress dalam browser dengan WordPress Playground dan WebAssembly.

Namun, PHP Wasm berjalan di browser dalam konteks JavaScript bahasa host. Di Chrome, JavaScript dan Wasm dijalankan di V8, yakni mesin JavaScript open source Google yang menerapkan ECMAScript seperti yang ditentukan dalam ECMA-262. Selain itu, V8 sudah memiliki pembersih sampah memori. Ini berarti developer yang memanfaatkan, misalnya, PHP yang dikompilasi ke Wasm, pada akhirnya mengirimkan implementasi pembersih sampah memori dari bahasa porta (PHP) ke browser yang sudah memiliki pembersih sampah, yang kedengarannya boros. Di sinilah WasmGC berperan.

Masalah lain dari pendekatan lama yang mengizinkan modul Wasm membangun GC sendiri di atas memori linear Wasm adalah bahwa tidak ada interaksi antara pembersih sampah Wasm sendiri dan pengumpul sampah bawaan dari bahasa yang dikompilasi ke Wasm, yang cenderung menyebabkan masalah seperti kebocoran memori dan upaya pengumpulan yang tidak efisien. Membiarkan modul Wasm menggunakan kembali GC bawaan yang ada akan menghindari masalah ini.

Melakukan porting bahasa pemrograman ke runtime baru dengan WasmGC

WasmGC adalah proposal dari WebAssembly Community Group. Implementasi MVP Wasm saat ini hanya mampu menangani angka, yaitu bilangan bulat dan float, dalam memori linear, dan dengan proposal jenis referensi yang diluncurkan, Wasm juga dapat menyimpan referensi eksternal. WasmGC kini menambahkan jenis heap struct dan array, yang berarti dukungan untuk alokasi memori non-linear. Setiap objek WasmGC memiliki jenis dan struktur tetap, yang memudahkan VM membuat kode yang efisien untuk mengakses kolomnya tanpa risiko depengoptimalan seperti yang dimiliki bahasa dinamis seperti JavaScript. Dengan demikian, proposal ini menambahkan dukungan yang efisien untuk bahasa terkelola tingkat tinggi ke WebAssembly, melalui jenis heap struct dan array yang memungkinkan compiler bahasa yang menargetkan Wasm untuk berintegrasi dengan pembersih sampah memori di VM host. Dalam istilah yang disederhanakan, ini berarti bahwa dengan WasmGC, mem-port bahasa pemrograman ke Wasm berarti pembersih sampah bahasa pemrograman tidak perlu lagi menjadi bagian dari port, tetapi pengumpul sampah yang ada dapat digunakan.

Untuk memverifikasi dampak nyata dari peningkatan ini, tim Wasm Chrome telah menyusun versi tolok ukur Fannkuch (yang mengalokasikan struktur data saat berfungsi) dari C, Rust, dan Java. Biner C dan Rust bisa berkisar antara 6.1 K hingga 9.6 K bergantung pada berbagai flag compiler, sedangkan versi Java jauh lebih kecil, yaitu hanya 2.3 K. C dan Rust tidak menyertakan pembersih sampah memori, tetapi mereka masih memaketkan malloc/free untuk mengelola memori, dan alasan Java lebih kecil di sini adalah karena tidak perlu memaketkan kode manajemen memori sama sekali. Ini hanyalah satu contoh spesifik, tetapi menunjukkan bahwa biner WasmGC memiliki potensi yang sangat kecil, dan ini bahkan sebelum dilakukan pekerjaan signifikan pada pengoptimalan ukuran.

Melihat cara kerja bahasa pemrograman yang di-port WasmGC

Wasm Kotlin

Salah satu bahasa pemrograman pertama yang telah ditransfer ke Wasm berkat WasmGC adalah Kotlin dalam bentuk Kotlin/Wasm. Demo, dengan kode sumber milik tim Kotlin, ditampilkan dalam listingan berikut.

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"

Sekarang Anda mungkin bertanya-tanya apa intinya, karena kode Kotlin di atas pada dasarnya terdiri dari JavaScript OM API yang dikonversi ke Kotlin. Versi ini mulai lebih masuk akal jika dikombinasikan dengan Multiplatform Compose, yang memungkinkan developer membangun aplikasi berdasarkan UI yang mungkin sudah mereka buat untuk aplikasi Kotlin Android. Lihat eksplorasi awal tentang hal ini dengan demo penampil gambar Kotlin/Wasm dan jelajahi kode sumber, juga dari tim Kotlin.

Dart dan Flutter

Tim Dart dan Flutter di Google juga menyiapkan dukungan untuk WasmGC. Pekerjaan kompilasi Dart-to-Wasm hampir selesai, dan tim sedang mengerjakan dukungan alat untuk mengirimkan aplikasi web Flutter yang dikompilasi ke WebAssembly. Anda dapat membaca status pekerjaan saat ini di dokumentasi Flutter. Demo berikut adalah Pratinjau WasmGC Flutter.

Pelajari WasmGC lebih lanjut

Postingan blog ini sedikit membahas permukaannya dan sebagian besar memberikan gambaran umum tentang WasmGC. Untuk mempelajari fitur ini lebih lanjut, buka link berikut:

Ucapan terima kasih

Banner besar oleh Gary Chan di Unsplash. Artikel ini ditinjau oleh Matthias Liedtke, Adam Klein, Joshua Bell, Alon Zakai, Jakob Kummerow, Clemens Backes, Emanuel Ziegler, dan Rachel Andrew.