nodejs中函数共享作用域导致的内存泄漏

关于一个Node.js内存泄漏函数的思考

一天在看一篇关于Node内存泄漏的博文中讲到一个会导致内存泄漏的函数一时没看懂于是就有了这篇文章:

  • 直接上菜
  • 共享父级作用域
  • 执行过程分析
  • 结束

直接上菜

var theThing = null;

var replaceThing = function () {
    var originalThing = theThing;
    var unused = function () {
        if (originalThing) // a reference to 'originalThing'
            console.log("hi");
    };
    theThing = {
        longStr: new Array(20*1024*1024).join(),
        someMethod: function () {
            console.log("message");
        }
    };
}
setInterval(replaceThing, 100);

乍眼一看这段代码多次运行并不会造成大量的内存泄漏,其中每次运行新分配内存会在函数执行完后由于theThing被重新赋值,而引用为null被GC掉,可当我运行这段代码时,它却迅速的占用了大量内存,并且还在一致持续增长;无疑这段代码导致已经导致了内存泄漏,Node的垃圾回收机制似乎失效了。

共享父级作用域 :

到这里我们需要明白一个Node中的概念函数“共享父级作用域”,我们进行断点调试可以看到:
这里写图片描述
函数‘unused’在内部引用了外部变量‘originalThing’,所以它的作用域里就会包含‘originalThing’,到这儿一点毛病都没有;
我们再来看同在一个地方被创建的函数‘someMethod’,很简单函数体里面没有引用外部变量;
这里写图片描述
当观察‘someMethod’的作用域时,我们发现其中也包含了‘originalThing‘也就是说他们的父级作用域是相同的,这就是Node中的函数“共享父级作用域“。

执行过程分析:

在每次函数执行完成时由于replaceThing没有被从setInterval里移除,它作用域里的变量“theThing”就不会被释放; 下次执行时,theThing不为null,originalThing的作用是为了让前一次分配的内存空间应用不为空,从而不被释放掉,这样才能持续消耗内存;unused在函数定义里面引用了originalThing,(虽然它没有从来被执行过)它的作用域里就有originalThing;接着给通过new一个Array开辟新的内存空间,并赋值给theThing,到这儿theThing在上一次执行分配内存空间的引用被移除,函数执行完成,局部变量originalThing的生命周期也应该结束,这时上一次分配的内存就应该被释放掉; 这段代码的秘密就在于此了,由于replaceThing一直在事件循环里等待被执行,所以它的作用域里就不被释放,作用域的变量theThing的内存也不会被释放,函数someMethod也就一直在内存里,由于函数someMethod和unused是在同意地方声明的,所以他们共享父级作用域,于是函数someMethod的作用域里也就有变量originalThing,即使unused函数每次执行后都会被释放,但unused函数的作用域里还有一个引用someMethod未被释放,所以引用的变量originalThing的内存不会被释放。形成了一个作用域的链式引用,导致上次执行时分配的内存空间无法被垃圾回收,以致内存泄漏。

结束

可不可以说node里变量是存活在Scopes里。如果所有被引用函数的作用域里没有这个块内存的引用,它才会被下次GC所释放。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值