Created
January 2, 2015 00:54
-
-
Save isaacs/f5920e5c6985f1e4a9dd to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // A "memory leak" means that, even after garbage collection, memory is still | |
| // not released to the system. Note that this is different from "using a lot | |
| // of memory". | |
| // | |
| // It's of course possible to do things that prevent garbage collection, which | |
| // is a memory leak in JavaScript. (As opposed to a memory leak in the | |
| // JavaScript implementation.) | |
| // | |
| // Even if you do not leak memory, and the implementation does not leak memory, | |
| // if you never hit V8's GC heuristic case, then you might keep *using* a lot | |
| // of memory that is not collected. | |
| // | |
| // In this example, we use the --expose_gc flag which creates a global gc() | |
| // function, so that we can explicitly trigger full garbage collection. Only | |
| // bother to do so right before getting the memory usage, so that we can get | |
| // as many iterations as possible between each test, and increase the | |
| // likelihood of finding some leaks. | |
| if (process.argv[2] === 'child') { | |
| child(); | |
| } else { | |
| parent(); | |
| } | |
| function parent () { | |
| var spawn = require('child_process').spawn; | |
| var c = spawn(process.execPath, ['--expose_gc', __filename, 'child']); | |
| var d = ''; | |
| c.stdout.setEncoding('utf8'); | |
| c.stdout.on('data', function (data) { | |
| d += data; | |
| }); | |
| var runtime = 25000; | |
| console.log('letting child run for %d ms', runtime); | |
| setTimeout(parse, runtime); | |
| function parse() { | |
| c.kill(); | |
| var results = []; | |
| d.trim().split('\n').forEach(function (line) { | |
| // console.error(line); | |
| var data = JSON.parse(line); | |
| results.push(data); | |
| }); | |
| // Just in case they came in out of order, though that | |
| // should never ever happen, I'm paranoid I guess. | |
| results = results.sort(function (a, b) { | |
| return a.when - b.when; | |
| }); | |
| // dump all the data. | |
| // console.error(results); | |
| console.log('RSS'); | |
| console.log(results.map(function (data) { | |
| return data.rss; | |
| }).join('\n')); | |
| console.log('----\nHEAP USED'); | |
| console.log(results.map(function (data) { | |
| return data.heapUsed; | |
| }).join('\n')); | |
| console.log('----\nHEAP TOTAL'); | |
| console.log(results.map(function (data) { | |
| return data.heapTotal; | |
| }).join('\n')); | |
| } | |
| } | |
| function child () { | |
| var testInterval = 1000; | |
| var total = 0; | |
| var timerTime = Date.now(); | |
| var timer = setTimeout(function X () { | |
| var now = Date.now(); | |
| total++; | |
| if (now - timerTime > testInterval) { | |
| gc(); | |
| var data = process.memoryUsage(); | |
| data.when = now; | |
| data.which = 'timeout'; | |
| data.total = total; | |
| console.log('%j', data); | |
| timerTime = now; | |
| } | |
| timer = setTimeout(X); | |
| }); | |
| var intervalTime = Date.now(); | |
| setInterval(function X () { | |
| var now = Date.now(); | |
| total++; | |
| if (now - intervalTime > testInterval) { | |
| gc(); | |
| var data = process.memoryUsage(); | |
| data.when = now; | |
| data.which = 'interval'; | |
| data.total = total; | |
| console.log('%j', data); | |
| intervalTime = now; | |
| } | |
| }); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Results on my system:
Doesn't look like a memory leak to me. And this is doing many many thousands of timeouts, and an interval that's firing many many thousands of times.
Another interesting test might be to run a whole lot of intervals and timers in parallel, and make sure that they don't leak when used all at the same time?