

jott - js_gc_in_wasm
source link: https://jott.live/markdown/js_gc_in_wasm
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

JavaScript Garbage Collection with WebAssembly is Possible Today
by @bwasti
tl;dr - Using WeakRefs, you can pretty easily implement automatic memory management for objects exposed from WebAssembly with JavaScript's garbage collector.
(Jump to the implementation here.)
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.
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.
Luckily, people have written utilities to make this placement problem a bit easier.
The most common of these are probably 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 passing small bits to JavaScript.
Individual values work well!
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.
Pointers do not work well.
This can of course be dealt with, but that's a hassle.
That barely looks like JavaScript...
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,
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.
It's important to also note that there's a proposal for WebAssembly involving typed heaps and other new semantics that would also enable garbage collection. This is unfortunately only in phase 2, which means it hasn't been implemented anywhere let alone shipped to most browsers.
The proposed typed heap would be amenable to GC.
WeakRefs
A viable solution that I've found is to spin on weak references 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.
class MyClass {
public:
MyClass(int hide) : hidden(hide) { }
int my_method() { return hidden * 2; }
private:
int hidden;
};
The steps are as follows:
- Expose the constructor and destructors for your class.
In C++:
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:
const { instance } = await WebAssembly.instantiateStreaming(fetch("./demo.wasm"));
const {
_MyClass__constructor,
_MyClass__destructor,
_MyClass__my_method
} = instance.exports;
- Implement a function that perpetually loops, clearing up any dead weak references
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
await new Promise(resolve => setTimeout(resolve, 100));
}
}
- (b) Expose the ability to add to that loop with a
manage
function
function manage(ptr, weakref, deleter) {
managed[ptr] = [weakref, deleter];
}
- Wrap your pointer in a JavaScript class and call
manage
on the object
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);
}
};
- Done!
Now you can use your objects as fully garbage collected JavaScript objects.
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
.
Recommend
-
126
Today we will see how we can interact with WebAssembly, from Go: how to execute WebAssembly bytecode from Go and how to generate WebAssembly bytecode with Go. But first of all: what is WebAssembly? WebAssembly Ac...
-
137
README.md ?✨ wasm-pack pack up the wasm and publish it to npm!
-
7
Optimizing Euclidean Distance Queries Mathematically I saw an interesting post over at lobste.rs about efficiently
-
4
Building Stuff From Source by @bwasti Probably the most valuable thing I've learned as a programmer is how to build things from source without getting (too) frustrated. *...
-
2
15x Faster TypedArrays: Vector Addition in WebAssembly @ 154GB/s by @bwasti Vector addition is the process of adding vector components: A→+B→=(a0+b0,a1+b1,…)A→+...
-
4
Trickery to Tame Big WebAssembly Binaries by @bwasti In light of the extremely popular
-
7
Python 3.11 is much faster than 3.8 by @bwasti This is awesome, but let's not get ahead of ourselves. R...
-
9
Machine Learning with Unix Pipes by @bwasti I contribute to an open source programmer-focused machine learning library c...
-
3
Over-Engineering an Emoji Webcam Filter with a Neural Network by @bwasti (
-
2
How to Get 1.5 TFlops of FP32 Performance on a Single M1 CPU Core by @bwasti (
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK