# JavaScript Garbage Collection with WebAssembly is Possible Today *by [@bwasti](https://twitter.com/bwasti)* **** tl;dr - Using [WeakRefs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakRef), you can pretty easily implement automatic memory management for objects exposed from WebAssembly with JavaScript's garbage collector. *(Keep in mind "automatic memory management" does not mean "garbage collection". This post omits any specific implementation choices. Jump to the code [here](https://jott.live/markdown/js_gc_in_wasm#weakrefs).)* ## What's the issue being solved? It's well known that JavaScript has garbage collection by default. WebAssembly doesn't have such a mechanism, as it operates at a much lower level. You only get a big slab of memory. Luckily you can resize it. <center> <img src="https://i.imgur.com/aBXfbmr.png" style="max-width:100%; max-height:300px;"/> </center> So how the heck do you do anything? Well, you can place things inside this slab of memory where ever you'd like. It's all yours. However, deciding ***where*** to place things isn't always obvious. <center> <img src="https://i.imgur.com/9szV0GW.png" style="max-width:100%; max-height:300px;"/> </center> Fortunately, people have written utilities to make this placement problem a bit easier. The most common of these are `malloc` and `free`. Often, they're written in C and that's easily compiled to WebAssembly. But then another problem arises: ***when*** do you actually call `free`? In many languages this is done for you automatically (such as with scoping rules). That's well and good if you're mostly working within the compiled language and only passing small bits to JavaScript. <center> <img src="https://i.imgur.com/zt1Nl4i.png" style="width:100%; max-width: 600px;"/> </center> <center><i>Individual values work well!</i></center> On the other hand, if you're dealing with big objects and want to pass around pointers, the moment you pass them to JavaScript, you lose all the niceties the compiled language had to offer. You might even end up with memory leaks. <center> <img src="https://i.imgur.com/qwoc4OC.png" style="width:100%; max-width: 600px;"/> </center> <center><i>Pointers do not work well.</i></center> This can of course be dealt with, but that's a hassle. <center> <img src="https://i.imgur.com/gwP10d1.png" style="width:100%; max-width: 600px;"/> </center> <center><i>That barely looks like JavaScript...</i></center> ## Not-quite-there Proposals Ideally we'd just register our pointers and some function to `free` them with JavaScript's garbage collection mechanism. This does exist to some degree with a [FinalizationRegistry](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/FinalizationRegistry), but my minimal testing has not shown it to work very reliably. There are no guarantees that finalization callbacks are actually called and I've found my code stuck waiting forever. <center> <img src="https://media1.giphy.com/media/QBd2kLB5qDmysEXre9/giphy.gif?cid=ecf05e479zxlyk2gd9hymhtw6o6a3pccqyo5ilazcgqq8f7y&rid=giphy.gif&ct=g" style="max-width:300px;"/> </center> It's important to also note that there's a [proposal](https://github.com/WebAssembly/gc/blob/main/proposals/gc/MVP.md) for WebAssembly involving typed heaps and other new semantics that would also enable garbage collection. This is unfortunately only in [phase 2](https://github.com/WebAssembly/proposals/blob/main/README.md), which means it hasn't been implemented anywhere let alone shipped to most browsers. <center> <img src="https://i.imgur.com/rTzOBAW.png" style="max-width:150px;padding-bottom:20px;"/> </center> <center><i>The proposed typed heap would be amenable to GC.</i></center> ## WeakRefs A viable solution that I've found is to spin on weak references (brrr) and `free` underlying pointers when the weak refs go null. (WeakRefs are supported in every major browser.) Below is an example in C++, but the concept should hold for any managed memory language. Here's the C++ class we'll expose: ```cpp class MyClass { public: MyClass(int hide) : hidden(hide) { } int my_method() { return hidden * 2; } private: int hidden; }; ``` The steps are as follows: 1. Expose the constructor and destructors for your class. In C++: ```clang extern "C" { EXPORT MyClass* _MyClass__constructor(int h) { return new MyClass(h); } EXPORT void _MyClass__destructor(MyClass* m) { delete m; } EXPORT int _MyClass__my_method(MyClass* m) { return m->my_method(); } } ``` and in JavaScript: ```js const { instance } = await WebAssembly.instantiateStreaming(fetch("./demo.wasm")); const { _MyClass__constructor, _MyClass__destructor, _MyClass__my_method } = instance.exports; ``` 2. Implement a function that perpetually loops, clearing up any dead weak references ```js const managed = {}; async function memory_management_loop() { while (true) { const keys = Object.keys(managed); for (let key of keys) { const [weakref, deleter] = managed[key]; if (!weakref.deref()) { // this object was garbage collected deleter(); // call the user provided deleter delete managed[key]; } } // cleanup every 100ms, or replace with window.requestIdleCallback() await new Promise(resolve => setTimeout(resolve, 100)); } } ``` 2. (b) Expose the ability to add to that loop with a `manage` function ```js function manage(ptr, weakref, deleter) { managed[ptr] = [weakref, deleter]; } ``` 3. Wrap your pointer in a JavaScript class and call `manage` on the object ```js class MyClass { constructor(h) { const data = _MyClass__constructor(h); // make sure the third argument does not reference `this` manage(data, new WeakRef(this), () => { _MyClass__destructor(data); // data is just a pointer :) }); this.data = data; } my_method() { return _MyClass__my_method(this.data); } }; ``` 4. Done! Now you can use your objects as fully garbage collected JavaScript objects. ```js for (let i = 0; i < 50; ++i) { const m = new MyClass(5 + i); console.log(m.my_method()); } ``` All 50 created `MyClass` objects go out of scope and will be cleaned up soon after the above code executes. ## Code Listing If you'd like to see the whole code used (pure `clang`!), please see this repo: https://github.com/bwasti/web_assembly_experiments/tree/main/memory_management You'll want to run `make` and then `python3 -m http.server` or equivalent to open `demo.html`. ## Building on this For one, this isn't exactly "production ready." But, it'll let you hack around one of the more annoying deficiencies WebAssembly/JavaScript interaction has today. Second, nothing here actually implements garbage collection for WebAssembly. We're just using the garbage collection mechanism in JavaScript as a way to call WebAssembly destructors at the appropriate (or, at least, *valid*) time. A more involved example would use something like C++'s shared pointers to implement ref-counting based cleanup inside the compiled code. One could also go a step further and actually run a background process to periodically cleanup memory (called from JavaScript of course). If we wanted to further integrate with JavaScript's garbage collection, WebAssembly would need a way to hold strong references on JS objects. This requires another layer of wrapping. Luckily, exposing JavaScript calls to WebAssembly is a straight-forward process: ```js function tell_js_to_decref(ptr) { delete held_refs[ptr]; } const { instance } = await WebAssembly.instantiateStreaming(fetch("./demo.wasm"), { env: { decref: ptr => tell_js_to_decref(ptr) } }); ``` And now we've got `tell_js_to_decref()` in WebAssembly as `decref`. ### Thanks for reading :) *If you'd like, you can follow me on Twitter here: [@bwasti](https://twitter.com/bwasti)*