Memory Management di WebAssembly
WebAssembly menggunakan linear memory — satu blok kontinu dari bytes yang bisa diakses dari Wasm dan JavaScript. Tidak ada garbage collector, tidak ada object reference — hanya raw bytes.
Linear Memory
;; Deklarasi memory: initial 1 page, max 10 pages
;; 1 page = 64KB
(module
(memory 1 10) ;; start 64KB, max 640KB
(export "memory" (memory 0))
;; Tulis angka ke memory offset 0
(func $write (export "write")
i32.const 0 ;; alamat (offset)
i32.const 42 ;; nilai
i32.store ;; simpan 4 bytes di offset 0
)
;; Baca angka dari memory offset 0
(func $read (export "read") (result i32)
i32.const 0 ;; alamat (offset)
i32.load ;; baca 4 bytes dari offset 0
)
)
Memory.grow
// Wasm memory bisa di-grow (tapi tidak di-shrink)
const memory = new WebAssembly.Memory({
initial: 1, // 1 page = 64KB
maximum: 100, // max 100 pages = 6.4MB
});
console.log(memory.buffer.byteLength); // 65536
memory.grow(2); // tambah 2 pages (128KB)
console.log(memory.buffer.byteLength); // 196608
// ⚠️ PENTING: setelah grow, buffer reference berubah!
// Selalu re-read memory.buffer setelah grow
let view = new Uint8Array(memory.buffer); // harus re-create
Typed Arrays sebagai View
// Berbagai "lensa" untuk melihat memory yang sama
const buffer = instance.exports.memory.buffer;
const bytes = new Uint8Array(buffer); // per byte
const ints = new Int32Array(buffer); // per 4 bytes (int)
const floats = new Float64Array(buffer); // per 8 bytes (double)
// Tulis integer di offset 0
ints[0] = 1000;
// Baca byte individual dari integer yang sama
console.log(bytes[0]); // 232 (low byte of 1000)
console.log(bytes[1]); // 3 (high byte of 1000)
// Transfer array besar ke Wasm
const data = new Float64Array([1.1, 2.2, 3.3, 4.4]);
const wasmFloats = new Float64Array(buffer, 0, data.length);
wasmFloats.set(data); // copy ke Wasm memory
Allocator Pattern
Untuk bahasa tanpa runtime (Rust, C), kamu perlu allocator untuk dynamic memory:
// Rust dengan wee_alloc (allocator kecil untuk Wasm)
// Cargo.toml: wee_alloc = "0.4"
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
// Sekarang Vec, String, Box, dll. bisa digunakan
Common Pitfalls
- Buffer detach — Setelah memory.grow(), semua TypedArray view menjadi invalid
- Memory leaks — Tanpa GC, kamu harus free memory manual (atau pakai Rust ownership)
- Alignment — i32.load butuh offset kelipatan 4, f64.load butuh kelipatan 8
- Out of bounds — Akses di luar memory = trap (crash). Selalu validasi offset.