什么是内存泄露?
程序运行的时候需要用到内存,对于持续运行的服务进程,必须要及时释放内存,否则,内存占用越来越高,将影响系统性能,甚至导致进程崩溃。
此时,有些程序已经结束进程,不再用到内存,但是没有及时释放内存,这就叫做内存泄露(memory leak)。
为解决内存泄露问题,大多数语言提供了自动内存管理,减轻程序员手动释放内存的负担,这被称为垃圾回收机制(garbage collector)。
JavaScript垃圾回收机制原理:
解决内存泄露,垃圾回收机制会定期(周期性)找到那些不再用到的内存(变量),然后释放其内存。
现在各大浏览器通常采用的垃圾回收机制有两种方式:引用计数、标记清除。
引用计数
该方式主要计算每一个数据(值)被引用的次数。 每当一个数据被一个变量或者其它引用一次,就对该数据的累计次数
+1
。每当减少一个引用次数,就
-1
。当引用次数为
时,就会销毁该数据,也就是把它当作垃圾来回收掉,从而释放内存。
上图中,左下角的两个值,没有任何引用,即引用次数为
,所以可以将其回收,释放内存。
但是,如果一个值不再需要了,但是引用计数却不为
,垃圾回收机制就无法释放这块内存,从而导致内存泄露。
上面代码中,数组
[1, 2, 3]
是一个值,会占用内存。变量
arr
是仅有的对这个值的引用,因此引用次数为
1
。尽管后面的代码没有
arr
,它还是持续占用内存。
如果增加一行代码,解除
arr
对
[1, 2, 3]
的引用,则引用次数为
,这块内存就哭被垃圾回收机制回收,释放内存。
上面代码中,
arr
重置为
null
,就解除了对
[1, 2, 3, 4]
的引用,引用次数变成了
,内存就可以释放出来了。
但是,通过引用计数方式来释放内存,有一个缺点,就是无法解决循环引用问题。
标记清除
js中最常用的垃圾回收方式就是标记清除。当变量进入环境时,例如,在一个函数中声明一个变量,就将这个变量标记为"
进入环境
",从逻辑上讲,永远不能释放进入环境变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到它们。而当变量离开环境时,则将其标记为"
离开环境
"。
function test(){
var a = 10; //被标记"进入环境"
var b = "hello"; //被标记"进入环境"
}
test(); //执行完毕后之后,a和b又被标记"离开环境",被回收
垃圾回收机制在运行的时候会给存储在内存中的所有变量都加上标记(可以是任何标记方式),然后,它会去掉处在环境中的变量及被环境中的变量引用的变量标记(闭包)。
而在此之后剩下的带有标记的变量被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。
最后垃圾回收机制到下一个周期运行时,将释放这些变量的内存,回收它们所占用的空间。
到目前为止,IE、Firefox、Opera、Chrome、Safari的js实现使用的都是标记清除的垃圾回收策略或类似的策略,只不过垃圾收集的时间间隔互不相同。
因此,并不是说有了垃圾回收机制,程序员就轻松了。你还是需要关注内存占用:那些很占空间的值,一旦不再用到,你必须检查是否还存在对它们的引用。如果是的话,就必须手动解除引用。
参考:
http://www.ruanyifeng.com/blog/2017/04/memory-leak.html
https://blog.csdn.net/oliver_web/article/details/53957021