求解一个 promise 内存泄漏问题

2016-06-22 17:10:03 +08:00
 iceiceshen24

被一个关于 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();
6131 次点击
所在节点    Node.js
15 条回复
bearice
2016-06-22 17:49:10 +08:00
C 并不是不泄露,只是泄露的少吧

https://gist.github.com/bearice/ac52f65e4a1b22c7074e0a2e2d818967
kenshinhu
2016-06-22 17:52:50 +08:00
@bearice 弱弱问问这个是怎样测试的
bearice
2016-06-22 17:58:38 +08:00
@kenshinhu node --log_gc --trace_gc test.js
xuzicn
2016-06-22 19:02:06 +08:00
并没有泄露啊
第一个代码不停的在压栈,不停的在构建执行上下文而且不释放。
第二个代码释放得很干净
第三个压栈的速度只有第一个压栈的一半。
mcfog
2016-06-22 19:18:21 +08:00
用很多内存和内存泄露是两回事情啊

内存泄露是“该回收没回收”,这段代码只是构造了“很长的调用栈,大量信息不该回收”的场景而已,算不上内存泄露。
Sparetire
2016-06-22 23:24:12 +08:00
我也觉得应该是用了很多内存而不是泄露,初学 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 要等两个,开销应该也小很多。
iceiceshen24
2016-06-23 10:13:30 +08:00
@bearice 你这个指令好厉害,受教了~但是我试了一遍你这个指令,发现 C 占用内存是一直没有变得呀,弱弱地说一哈之前一直用 top 看内存泄漏
breeswish
2016-06-23 10:38:25 +08:00
iceiceshen24
2016-06-23 10:40:43 +08:00
@breeswish 这个我感觉他没讲清楚
xuzicn
2016-06-23 13:59:21 +08:00
@breeswish 他这个文章有错。一会我补一篇来讲明问题
@Sparetire 你的前半段理解我认同,后面的也有不准确的地方
iceiceshen24
2016-06-23 15:34:46 +08:00
@xuzicn nice !
xuzicn
2016-06-24 11:21:08 +08:00
@iceiceshen24
@breeswish
@Sparetire

懒得写文了,只需要在他的 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 (r) {
global.gc()
console.log(111)
printMemory();
});
})();
Sparetire
2016-06-25 00:52:14 +08:00
@xuzicn 3Q, 明天试试
iceiceshen24
2016-06-30 10:46:33 +08:00
@xuzicn 3Q~~
poke707
2016-11-28 21:37:06 +08:00
看 co 的一些代码发现 https://github.com/tj/co/issues/180

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

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/287602

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX