This is something weird I noticed. The following code shouldn't blow the memory as a WeakSet
is used and obviously no other references linger around:
'use strict';
require('babel-polyfill');
const s = new WeakSet();
for (let i = 0 ; ; i++) {
s.add({});
if (i % 100000 === 0)
console.log(`${i} :${process.memoryUsage().heapUsed}`);
}
(SCCE github repo here).
And yet blow the memory it does (in Node v4.3.2
with Babel
transpiling):
<--- Last few GCs --->
165 ms: Scavenge 13.6 (48.0) -> 13.6 (48.0) MB, 14.4 / 0 ms [allocation failure].
189 ms: Scavenge 14.4 (48.0) -> 14.4 (52.0) MB, 17.6 / 0 ms [allocation failure].
340 ms: Scavenge 37.5 (68.0) -> 37.5 (68.0) MB, 35.2 / 0 ms [allocation failure].
380 ms: Scavenge 38.3 (68.0) -> 38.3 (76.0) MB, 35.5 / 0 ms [allocation failure].
567 ms: Scavenge 53.5 (76.0) -> 53.4 (77.0) MB, 74.6 / 0 ms [allocation failure].
<--- JS stacktrace --->
==== JS stack trace =========================================
Security context: 0x228b1a4b4629 <JS Object>
1: add [native weak-collection.js:~92] [pc=0x2b4d202650b5] (this=0x386dbd0641f9 <JS WeakSet>,l=0x389216b5e19 <an Object with map 0x21f1c4616e79>)
2: /* anonymous */ [/home/mperdikeas/weak-set-blows-memory/es5/app.js:~1] [pc=0x2b4d20269023] (this=0x386dbd064221 <an Object with map 0x3193b8408829>,exports=0x228b1a4041b9 <undefined>,require=0x228b1a4041b9 <undefined>,module=0x228b1a4041b9 ...
FATAL ERROR: invalid table size Allocation failed - process out of memory
Aborted (core dumped)
npm ERR! Linux 3.16.0-48-generic
npm ERR! argv "/usr/bin/nodejs" "/usr/bin/npm" "run" "start"
npm ERR! node v4.3.2
npm ERR! npm v2.14.12
npm ERR! code ELIFECYCLE
npm ERR! [email protected] start: `node es5/app.js`
npm ERR! Exit status 134
npm ERR!
Update The bug has been fixed today.The fix is moving to v8 5.0 which is what Node 6.0 is using - so in a few weeks you'll have a Node version that has it fixed.
This is an open bug in v8. Your code as stated should work just fine. The problem is basically that v8 doesn't do full garbage collection but only minimal garbage collection in this case.
This is not working fine in Chrome, the only reason it is not leaking in Chrome is because other objects are created and can be freed - those objects being possible to free triggers full garbage collection which also cleans the WeakSet
.
WeakSet
comes natively, there is a clever polyfill for it in core-js
but it is not actually 100% weak, used here or is relevant.
Following code doesn't leak in Chrome 49:
'use strict';
const s = new WeakSet();
let j = 0;
function f(){
for (let i = 0 ; i<100000; i++) {
s.add({});
}
console.log(`${j++} :${0|(performance.memory.usedJSHeapSize/(1024*1024))}MB`);
return Promise.resolve(null).then(f);
}
f()
So it seems like a bug in Node.js (it's reproducible in v5.1.10 too). I have filled an issue for this in the Node.js bugtracker.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With