js闭包必定引起内存泄漏吗

什么是内存泄漏

像c,c++这种语言,有个free函数,用来释放动态分配的内存.如果某个或某些动态分配的变量没有了作用,并且占用的内存很大,足以影响整个程序的运行.这就叫内存泄漏.同理,像js,java这种语言,由v8和jvm等来帮助管理这部分内存.他们管理的时候都不会释放你有可能用到的内存.
以下以js为例.v8并不知道你需要用到哪些对象.所以他只能通过一些条件来判断出你绝对不会使用的对象.比如如果一个对象已经没有了引用,那你肯定就无法用js来操作这个对象了,v8就认为这个对象占用的内存是可以释放的了.但他并不一定会立即释放,gc(释放垃圾内存)是有性能损耗的.所有v8的gc也是有一定算法的.假设你有这样一段代码:
let arr = new Array(1000000)
即使你下面不会使用到arr,v8也绝对不会释放掉arr占用的内存,因为还有arr这个引用.怎么来证明这一点呢.前面说过v8的gc时机是有一定的算法的.不过node有一个global.gc()方法,能够全局gc,但是需要--expose-gc参数来执行node进程.node还有一个方法,process.memoryUsage()能够查看这个node进程的内存占用情况.

输入图片说明

代码1中演示了这两个方法的使用.可以看出第一次强制全局gc,并没有将这个1000000长度空数组占用的内存释放掉.内存从最开始的5M上升到了12M.将这个数组的引用去掉后,再次gc,便能释放这部分内存.
那么问题来了,假设我写了段代码,并没有将arr置为null,并且以后也不会使用这个arr.那我的代码算是内存泄漏了吗?可能是吧,那我的arr所引用的数组长度不是1000000了,而是0.arr=[].那我这还算是内存泄漏吗?我觉得不是.感觉内存泄漏是个辩证的看法吧,如果某个不会使用的对象,我们在代码里把他写成了不能让v8释放,这就构成了内存泄漏,我觉得谁都写过内存泄漏的代码.要是有一个对象不用了,我就要将他的所有引用置为空.那我还要v8的垃圾回收做什么,我置位空,v8也不见得回收他,倒不如用c语言的free好了.
所以我觉得只有不必要的大内存,由于代码问题,造成v8不能回收.影响了程序的性能,这才叫内存泄漏.而本文要讨论的实际是: js闭包,必定会引起部分内存不能释放吗?

什么是js闭包

学过js的人应该都了解,js的变量作用域.外部作用域,不能访问内部作用域的变量.反过来可以.而闭包,引用朴灵大大的话,便是在js中,实现外部作用域访问内部作用域中变量的方法叫做闭包.
输入图片说明 在代码2中,内部引用inLead并不能在外部直接访问,但是getInLeadFn作用域里可以访问,包括返回的匿名函数.将这个匿名函数通过执行getInLeadFn引用到外部作用域,便可以间接访问inLead.

js闭包会引起内存不能释放吗?

记得刚面试的时候,就有面试官问过我,闭包的危害.当时自己没有回答上来,面试官当时告诉我,会引起内存泄漏.当时自己对内存的概念不太关注.就这样记住了,到后来,越来越怀疑这句话.百度,网上也有的言论是这样说的.还是实践出真知.
输入图片说明 写完整个闭包,可以看出,内存从5M到了14M,gc也还剩13M.但是只要将outLeadFn置位null,即将内部作用域的函数引用去掉,再gc,便能释放内存.
有没有看到,这和代码1是类似的.所以我觉得js闭包,只不过是把内部作用域的对象延伸到了外部罢了,就像外部作用域的对象有引用内存不能gc,通过js闭包得到的内部对象如果有引用,也不能gc.
有人会说,inLead这个引用还在指向这个数组呢.引用的内存分配在栈上,对象的内存分配在堆上.当函数执行完之后,函数栈上的内存便可以释放了.当fn这个函数执行完之后,fn里的inLead便被销毁了,不需要等待gc.这也是为什么在外层作用域不能再找到inLead了.

总结

**js闭包,不会引发内存不能释放,更不会引起内存泄漏.只不过是把内部作用域的对象延伸到了外部罢了,想要通过gc释放这个对象的内存,只需要将引用去掉即可. **有问题,希望指出,谢谢!

猜你喜欢

转载自my.oschina.net/u/3361610/blog/1606127