NodeJS, Promises and performance

最后都变了- 提交于 2019-11-28 01:40:22
rsp

It's not that the garbage collector doesn't work optimally but that it doesn't work at all - you don't give it any chance to.

When developing the tco module that does tail call optimization in Node i noticed a strange thing. It seemed to leak memory and I didn't know why. It turned out that it was because of few console.log() calls in various places that I used for testing to see what's going on because seeing a result of recursive call millions levels deep took some time so I wanted to see something while it was doing it.

Your example is pretty similar to that.

Remember that Node is single-threaded. When your computations run, nothing else can - including the GC. Your code is completely synchronous and blocking - even though it's generating millions of promises in a blocking manner. It is blocking because it never reaches the event loop.

Consider this example:

var a = 0, b = 10000000;

function numbers() {
  while (a < b) {
    console.log("Number " + a++);
  }
}

numbers();

It's pretty simple - you want to print 10 million numbers. But when you run it it behaves very strangely - for example it prints numbers up to some point, and then it stops for several seconds, then it keeps going or maybe starts trashing if you're using swap, or maybe gives you this error that I just got right after seeing the Number 8486:

FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - process out of memory
Aborted

What's going on here is that the main thread is blocked in a synchronous loop where it keeps creating objects but the GC has no chance to release them.

For such long running tasks you need to divide your work and get into the event loop once in a while.

Here is how you can fix this problem:

var a = 0, b = 10000000;

function numbers() {
  var i = 0;
  while (a < b && i++ < 100) {
    console.log("Number " + a++);
  }
  if (a < b) setImmediate(numbers);
}

numbers();

It does the same - it prints numbers from a to b but in bunches of 100 and then it schedules itself to continue at the end of the event loop.

Output of $(which time) -v node numbers1.js 2>&1 | egrep 'Maximum resident|FATAL'

FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - process out of memory
    Maximum resident set size (kbytes): 1495968

It used 1.5GB of memory and crashed.

Output of $(which time) -v node numbers2.js 2>&1 | egrep 'Maximum resident|FATAL'

    Maximum resident set size (kbytes): 56404

It used 56MB of memory and finished.

See also those answers:

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!