河北专业做网站,建筑新型模板,中国十大购物网站,上海中学校服如果没有有意识地编写代码来避免内存泄漏#xff0c;那么内存泄漏几乎是不可避免的JavaScript问题。它们的发生方式有很多种#xff0c;所以我们只重点介绍几种比较常见的情况。
内存泄漏实例1:对不存在的对象的悬空引用
考虑以下代码:
var theThing null;
var replaceTh…如果没有有意识地编写代码来避免内存泄漏那么内存泄漏几乎是不可避免的JavaScript问题。它们的发生方式有很多种所以我们只重点介绍几种比较常见的情况。
内存泄漏实例1:对不存在的对象的悬空引用
考虑以下代码:
var theThing null;
var replaceThing function () {var priorThing theThing; var unused function () {// unused是priorThing被引用的唯一地方。// 但unused从未被调用过if (priorThing) {console.log(hi);}};theThing {longStr: new Array(1000000).join(*), // 创建一个1MB的对象someMethod: function () {console.log(someMessage);}};
};
setInterval(replaceThing, 1000); // 每秒钟调用一次 replaceThing。
如果你运行上述代码并监测内存使用情况你会发现你有一个明显的内存泄漏每秒泄漏整整一兆字节而即使是手动垃圾收集器GC也无济于事。因此看起来我们每次调用 replaceThing 都会泄漏 longStr。但是为什么呢
每个theThing对象包含它自己的1MB longStr对象。每一秒钟当我们调用 replaceThing 时它都会在 priorThing 中保持对先前 theThing 对象的引用。
但是我们仍然认为这不会是一个问题因为每次通过先前引用的priorThing将被取消引用当priorThing通过priorThing theThing;被重置时。而且只在 replaceThing 的主体和unused的函数中被引用而事实上从未被使用。
因此我们又一次想知道为什么这里会有内存泄漏。
为了理解发生了什么我们需要更好地理解JavaScript的内部工作。实现闭包的典型方式是每个函数对象都有一个链接到代表其词法作用域的字典式对象。如果在replaceThing里面定义的两个函数实际上都使用了priorThing那么它们都得到了相同的对象就很重要即使priorThing被反复赋值所以两个函数都共享相同的词法环境。但是一旦一个变量被任何闭包使用它就会在该作用域内所有闭包共享的词法环境中结束。而这个小小的细微差别正是导致这个可怕的内存泄露的原因。
内存泄漏实例2循环引用
考虑下面代码
function addClickHandler(element) {element.click function onClick(e) {alert(Clicked the element.nodeName)}
}
这里onClick有一个闭包保持对element的引用通过element.nodeName。通过将onClick分配给element.click循环引用被创建即 element → onClick → element → onClick → element...
有趣的是即使 element 被从DOM中移除上面的循环自引用也会阻止 element 和onClick被收集因此会出现内存泄漏。
避免内存泄漏:要点
JavaScript的内存管理尤其是垃圾回收主要是基于对象可达性的概念。
以下对象被认为是可达的被称为 根: 从当前调用堆栈的任何地方引用的对象即当前被调用的函数中的所有局部变量和参数以及闭包作用域内的所有变量 所有全局变量
只要对象可以通过引用或引用链从任何一个根部访问它们就会被保留在内存中。
浏览器中有一个垃圾收集器它可以清理被无法到达的对象所占用的内存换句话说当且仅当GC认为对象无法到达时才会将其从内存中删除。不幸的是很容易出现不再使用的 僵尸 对象但GC仍然认为它们是 可达的。