Nodejs 求解一个 promise 内存泄漏问题

Nodejs 求解一个 promise 内存泄漏问题

被一个关于 promise 内存泄漏的问题所困扰,希望大家可以帮忙解惑~

A 内存泄漏

'use strict';

var i = 0; function run() { return new Promise(function(resolve) { i++; setTimeout(function() { if (i === 10000 * 10) return resolve(); resolve(run()); }, 0); }) .then(function() {}); } run();

B 内存不泄露

与 A 相比即将 function run()中的第一个 return 去掉。

'use strict';

var i = 0; function run() { new Promise(function(resolve) { i++; setTimeout(function() { if (i === 10000 * 10) return resolve(); resolve(run()); }, 0); }) .then(function() {}); } run();

C 内存不泄露

与 A 相比将中的.then(function() {})去掉。

'use strict';

var i = 0; function run() { return new Promise(function(resolve) { i++; setTimeout(function() { if (i === 10000 * 10) return resolve(); resolve(run()); }, 0); }); } run();


16 回复

弱弱问问这个是怎样测试的

node --log_gc --trace_gc test.js

并没有泄露啊
第一个代码不停的在压栈,不停的在构建执行上下文而且不释放。
第二个代码释放得很干净
第三个压栈的速度只有第一个压栈的一半。

用很多内存和内存泄露是两回事情啊

内存泄露是“该回收没回收”,这段代码只是构造了“很长的调用栈,大量信息不该回收”的场景而已,算不上内存泄露。

我也觉得应该是用了很多内存而不是泄露,初学 Promise ,回忆一下死去的操作系统和编译原理知识。。不知道对不对哈。
记得 Promise A+规范是说 resolve 的参数如果是一个 Promise 会等这个状态完成。
对于 AC, 每一个 run 都要等待下一个 run 的 Promise 的状态及它的 then 的状态确定下来,应该是一直不停压栈直到最后一个 run 执行完才退栈,而 B 的 run 没有 return ,等于是 resolve(undefined),每一个 run 执行完,里面的 Promise 的状态就确定了,执行完一个 run 就退一个栈而不用等到最后一个 run 执行完才退栈。
而 A 和 C, 虽然都要等最后一个 run 执行完,但 C 每个 run 只要等一个 Promise 状态确定,而 A 要等两个,开销应该也小很多。

你这个指令好厉害,受教了~但是我试了一遍你这个指令,发现 C 占用内存是一直没有变得呀,弱弱地说一哈之前一直用 top 看内存泄漏

这个我感觉他没讲清楚

他这个文章有错。一会我补一篇来讲明问题
你的前半段理解我认同,后面的也有不准确的地方





懒得写文了,只需要在他的 A 段代码后,强制 gc 一次就可以看到区别。运行 node --expose-gc test.js ,最后一次 print 的内存比第二个 print 还少。


(function() {
function printMemory() {
console.log(process.memoryUsage())
}

// 记录 Promise 链的长度
var i = 0;
function run() {
return new Promise(function(resolve) {
// 每增加 10000 个 Promise 打印一次内存使用情况
if (i % 100 === 0) printMemory();
i++;
// 模拟一个异步操作
setTimeout(function() {
// 1000 个 Promise 之后退出
if(i === 100 * 10) return resolve();
// 如果 resolve 的参数是一个 Promise ,外层 Promise 将接管这个 Promise 的状态,构成嵌套 Promise
resolve(run());
}, 0);
}).then(function() {
// console.log(j);
return true;
});
}
run().then(function ® {
global.gc()
console.log(111)
printMemory();
});
})();

3Q, 明天试试

看 co 的一些代码发现 https://github.com/tj/co/issues/180

在一些内部是死循环的生成器中,相当于楼主的例子,是可以永远不 resolve / reject 的。那么 A 中串联的所有 promise 都得不到释放了, B 只有外层一个 promise 没有释放。

在Node.js中,处理Promise时内存泄漏通常是由于未正确管理资源(如未释放的事件监听器、未解决的Promise链等)引起的。以下是一些常见的内存泄漏原因及解决策略,并附上简单的代码示例。

常见原因及解决策略

  1. 未解决的Promise: 如果Promise链中的某个环节没有正确处理(即没有.then().catch().finally()),可能会导致内存泄漏。

    function fetchData() {
        return new Promise((resolve, reject) => {
            // 模拟异步操作
            setTimeout(() => resolve('data'), 1000);
        });
    }
    
    fetchData()
        .then(data => {
            console.log(data);
        })
        .catch(err => {
            console.error(err); // 确保捕获错误
        });
    
  2. 未释放的事件监听器: 在Promise中注册的事件监听器如果没有在适当的时候移除,也可能导致内存泄漏。

    const EventEmitter = require('events');
    const emitter = new EventEmitter();
    
    function createListener() {
        const listener = () => console.log('Event triggered');
        emitter.on('event', listener);
        // 确保在不再需要时移除监听器
        return () => emitter.removeListener('event', listener);
    }
    
    const cleanup = createListener();
    // 在适当的时候调用cleanup()
    cleanup();
    

确保所有Promise都被正确处理,并适时移除不再需要的事件监听器,是防止Node.js应用中Promise内存泄漏的关键。

回到顶部