long.js: memory leak when WASM used in conjunction with Jest

There’s a memory leak outlined here when the wasm version of this module is used in conjunction with Jest (which cleans up state between each test run).

It seems like perhaps new WebAssembly.Module is not being appropriately garbage collected; this might ultimately be an issue with the WASM implementation in Node.js, if so we can keep kicking this issue around 😝

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 1
  • Comments: 15 (7 by maintainers)

Most upvoted comments

The node module already does that, but not only the WASM module but also the WASM instance. In a normal node environment, this works just fine because a node module is created exactly once (i.e. every new Long uses the module-global memoized WASM instance already). The issue arises where a library like jest tries hard to unload the entire long module and require it again (or anything else that would make top-level statements within the module execute multiple times), which node wouldn’t do by itself. My assumption is that this is indeed a node or v8 bug.

I’ve saw a similar memory leak before when using eval or other ways to create new scripts in v8 (new Function, new Script). The Source Code seem to be kept alive by the inspector forever.

e. g. here is a v8 issue about that: https://bugs.chromium.org/p/v8/issues/detail?id=7527

Seems like the same is true for WASM. Makes sense as it’s another kind of script which is visible in the inspector.

You can’t blame long.js for this problem, because it only allocates a single WASM module. This is only a problem because jest is reloads node modules multiple times, which is unusual for node.js. Anyway this seem to be to be v8 bug.

@tiagonapoli awesome 👍 I think we can close this then?

Also, the crash goes away when moving compilation out of the loop, leaving instantiation within:

// wat.js
var val = 1;
var mod = new WebAssembly.Module(new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 13, 2, 96, 0, 1, 127, 96, 4, 127, 127, 127, 127, 1, 127, 3, 7, 6, 0, 1, 1, 1, 1, 1, 6, 6, 1, 127, 1, 65, 0, 11, 7, 50, 6, 3, 109, 117, 108, 0, 1, 5, 100, 105, 118, 95, 115, 0, 2, 5, 100, 105, 118, 95, 117, 0, 3, 5, 114, 101, 109, 95, 115, 0, 4, 5, 114, 101, 109, 95, 117, 0, 5, 8, 103, 101, 116, 95, 104, 105, 103, 104, 0, 0, 10, 191, 1, 6, 4, 0, 35, 0, 11, 36, 1, 1, 126, 32, 0, 173, 32, 1, 173, 66, 32, 134, 132, 32, 2, 173, 32, 3, 173, 66, 32, 134, 132, 126, 34, 4, 66, 32, 135, 167, 36, 0, 32, 4, 167, 11, 36, 1, 1, 126, 32, 0, 173, 32, 1, 173, 66, 32, 134, 132, 32, 2, 173, 32, 3, 173, 66, 32, 134, 132, 127, 34, 4, 66, 32, 135, 167, 36, 0, 32, 4, 167, 11, 36, 1, 1, 126, 32, 0, 173, 32, 1, 173, 66, 32, 134, 132, 32, 2, 173, 32, 3, 173, 66, 32, 134, 132, 128, 34, 4, 66, 32, 135, 167, 36, 0, 32, 4, 167, 11, 36, 1, 1, 126, 32, 0, 173, 32, 1, 173, 66, 32, 134, 132, 32, 2, 173, 32, 3, 173, 66, 32, 134, 132, 129, 34, 4, 66, 32, 135, 167, 36, 0, 32, 4, 167, 11, 36, 1, 1, 126, 32, 0, 173, 32, 1, 173, 66, 32, 134, 132, 32, 2, 173, 32, 3, 173, 66, 32, 134, 132, 130, 34, 4, 66, 32, 135, 167, 36, 0, 32, 4, 167, 11]));

function makeGarbage() {
  var wasm = new WebAssembly.Instance(mod, {}).exports;
  wasm.mul(val, 0, 1, 0);
  return wasm.get_high();
}

for (let i = 0; ; ++i) {
  val += makeGarbage();
  if (!(i % 1000)) console.log(val);
}