浏览器怎么进行垃圾回收?
- 什么是垃圾
- 不再需要的变量即为垃圾
- 全局变量随时可能用到,所以一定不是垃圾
- 如何捡垃圾
-
标记空间可达值
何为可达: 从根节点出发遍历所有对象,如果对象是可遍历的就是 可达(reachable) 的,反之就是 不可达(unreachable) 的。
-
回收不可达 的值所占据的内存。
- 什么时候捡垃圾
- 分代收集、增量收集、闲时收集。
浏览器中不同类型变量的内存都是何时释放?
Javascritp 中类型:值类型,引用类型。
-
引用类型
在没有引用之后,通过 V8 自动回收。
-
值类型
如果处于闭包的情况下,要等闭包没有引用才会被 V8 回收。 非闭包的情况下,等待 V8 的新生代切换的时候回收。
哪些情况会导致内存泄露?如何避免?
-
意外的全局变量
function foo(arg) { bar = "this is a hidden global variable"; }
bar 没被声明,会变成一个全局变量,在页面关闭之前不会被释放。
另一种意外的全局变量可能由
this
创建:function foo() { this.variable = "potential accidental global"; } // foo 调用自己,this 指向了全局对象(window) foo();
在 JavaScript 文件头部加上
use strict
,可以避免此类错误发生。启用严格模式解析 JavaScript,避免意外的全局变量。 -
被遗忘的计时器或回调函数
var someResource = getData(); setInterval(function() { var node = document.getElementById('Node'); if(node) { // 处理 node 和 someResource node.innerHTML = JSON.stringify(someResource)); } }, 1000);
这样的代码很常见,如果
id
为 Node 的元素从 DOM 中移除,该定时器仍会存在,同时因为回调函数中包含对someResource
的引用,定时器外面的someResource
也不会被释放。 -
闭包
function bindEvent() { var obj = document.createElement("xxx"); obj.onclick = function () { // Even if it is a empty function }; }
闭包可以维持函数内局部变量,使其得不到释放。上例定义事件回调时,由于是函数内定义函数,并且内部函数的事件回调引用外部函数,形成了闭包。可以将事件处理函数定义在外面,或者在定义事件处理函数的外部函数中,删除对 dom 的引用:
// 将事件处理函数定义在外面 function bindEvent() { var obj = document.createElement("xxx"); obj.onclick = onclickHandler; } // 或者在定义事件处理函数的外部函数中,删除对dom的引用 function bindEvent() { var obj = document.createElement("xxx"); obj.onclick = function () { // Even if it is a empty function }; obj = null; }
-
没有清理的 DOM 元素引用 有时保存 DOM 节点内部数据结构很有用。假如你想快速更新表格的几行内容,把每一行 DOM 存成 JSON 键值对或者数组很有意义。此时,同样的 DOM 元素存在两个引用:一个在 DOM 树中,另一个在字典中,删除这些行时,需要把两个引用都清除。
var elements = { button: document.getElementById("button"), image: document.getElementById("image"), text: document.getElementById("text"), }; function doStuff() { image.src = "http://some.url/image"; button.click(); console.log(text.innerHTML); } function removeButton() { document.body.removeChild(document.getElementById("button")); // 此时,仍旧存在一个全局的 button 的引用 // elements对象,button 元素仍旧在内存中,不能被 GC 回收。 // 执行elements["button"] = null }
虽然我们用
removeChild
移除了 button,但是还在elements
对象里保存着 button 的引用,换言之,DOM 元素还在内存里面。