Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

adding to `WeakSet` and yet managing to blow memory

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! 
like image 952
Marcus Junius Brutus Avatar asked Apr 08 '16 15:04

Marcus Junius Brutus


2 Answers

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.

like image 100
Benjamin Gruenbaum Avatar answered Nov 07 '22 08:11

Benjamin Gruenbaum


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.

like image 22
Ginden Avatar answered Nov 07 '22 09:11

Ginden