尽管垃圾收集器使用起来非常方便,但是它们也有自己的一套标准,其中之一是不确定性. 换句话说,垃圾回收是不可预测的. 您真的不知道何时收集,这意味着在某些情况下,该程序将使用更多的内存,尽管实际上是需要的. 在其他情况下,在特别敏感的应用程序中,可能会出现短暂的暂停. 尽管不确定性意味着无法确定何时收集,但大多数垃圾回收在分配过程中实现了共享回收的通用模式. 如果不执行分配,则大多数垃圾回收将保持空闲状态. 如以下内容:
执行大量分配. 这些元素中的大多数(或所有这些元素)都标记为不可访问(假设我们指向不再需要的缓存的引用). 没有进一步的分配.
在这种情况下,大多数垃圾回收将不执行任何回收工作. 换句话说,即使没有可用的引用来收集,收集器也不会收集它们. 尽管这并不是严格意义上的泄漏,但仍会导致内存使用率比平常高.
什么是内存泄漏?
内存泄漏是一种情况,当不再需要应用程序使用的内存片段时,它们将无法返回到操作系统或可用的内存池中.
编程语言具有不同的内存管理方法. 但是,是否使用内存的某个部分实际上是一个不确定的问题. 换句话说,只有开发人员才能清楚知道是否需要将一块内存返回给操作系统.
四个常见的JavaScript内存泄漏
1: 全局变量
JavaScript以一种有趣的方式处理未声明的变量: 当引用未声明的变量时,将在全局对象中创建一个新变量. 在浏览器中,全局对象将是window,这意味着
function foo(arg) {
bar = "some text";
}
等同于:
function foo(arg) {
window.bar = "some text";
}
bar只是引用foo函数中的一个变量. 如果您不使用var声明,则会创建一个额外的全局变量. 在上述情况下,不会造成大问题. 但是,如果是下面的情况.
您也可能会意外地创建一个全局变量:
function foo() {
this.var1 = "potential accidental global";
}
// Foo called on its own, this points to the global object (window)
// rather than being undefined.
foo( );
您可以通过添加“ use strict”来避免此错误;在JavaScript文件的开头. 此方法将启用严格的JavaScript解析模式,以防止意外创建全局变量.
意外的全局变量当然是一个问题. 通常,您的代码会受到显式全局变量的影响,并且这些全局变量无法在垃圾回收器中收集. 需要特别注意用于临时存储和处理大量信息的全局变量. 如果必须使用全局变量来存储数据,请确保将其分配为空值,或在完成后重新分配.
2: 忘记了计时器或回调

以下是setInterval的示例,该示例也经常在JavaScript中使用.
对于提供监视功能的库以及接受回调的其他工具,通常确保当实例无法访问时所有回调引用都变得不可访问. 但是以下代码是一个例外:
var serverData = loadData();
setInterval(function() {
var renderer = document.getElementById('renderer');
if(renderer) {
renderer.innerHTML = JSON.stringify(serverData);
}
}, 5000); //This will be executed every ~5 seconds.
上面的代码段显示了使用参考节点或不再需要的数据的计时器的结果.
渲染器对象可能在某个时候被替换或删除,这使得间隔处理程序封装的块变得多余. 如果发生这种情况,则将不收集处理程序或其依赖项,因为需要首先停止间隔. 所有这些归因于将不会收集存储和处理负载数据的serverData的原因.
使用监视器时,需要确保进行明确的通话以删除它们.
幸运的是,大多数现代浏览器都会为您执行此操作: 即使您忘记删除侦听器,当无法访问被监视的对象时,它们也会自动收集监视处理器. 这是过去某些浏览器无法处理的情况(例如旧版IE6).
看下面的例子:
var element = document.getElementById('launch-button');
var counter = 0;
function onClick(event) {
counter++;
element.innerHtml = 'text ' + counter;
}
element.addEventListener('click', onClick);
// Do stuff
element.removeEventListener('click', onClick);
element.parentNode.removeChild(element);
// Now when element goes out of scope,
// both element and onClick will be collected even in old browsers // that don't handle cycles well.
由于现代浏览器支持垃圾收集机制,当一个节点不可访问时,您不再需要调用removeEventListener,因为垃圾收集机制将正确处理这些节点.
如果使用的是jQueryAPI(其他库和框架也支持此功能),则还可以在不使用节点之前删除侦听器. 即使该应用程序在旧版本的浏览器上运行,该库也会确保没有内存泄漏.
3: 关闭
JavaScript开发的关键方面是闭包. 闭包是一个内部函数,可以访问外部(闭包)函数的变量. 由于JavaScript运行时的实现细节,可能以以下形式出现内存泄漏:
var theThing = null;
var replaceThing = function(){
var originalThing = theThing;
var unused = function(){
if(originalThing)//对'originalThing'的引用
console.log(“hi”);
};
theThing = {
longStr:new Array(1000000).join('*'),
someMethod:function(){
console.log(“message”);
}
};
};
setInterval(replaceThing,1000);
一旦调用replaceThing,Thing将获得一个由大数组和新闭包(someMethod)组成的新对象. 但是,originalThing将由未使用的变量(这是前一个调用变量replaceThing中的Thing)持有的闭包引用. 要记住的事情是,一旦在同一个父范围内为闭包创建了闭包的范围,就将共享该范围.
在这种情况下,闭包创建的作用域将与未使用的对象共享someMethod. 但是,未使用的文件具有originalThing参考. 即使从未使用过未使用的东西,某些方法也可以通过Thing在整个范围之,并且未使用必须引用originalThing才能使其他活动保持活动状态(两个闭包之间的整个共享范围). 这样可以防止其被收集.
所有这些都可能导致相当大的内存泄漏. 当以上代码片段反复运行时,您将看到内存使用率不断增加. 当垃圾收集器运行时,其内存大小不会缩小. 在这种情况下,将创建一个闭包的链表,每个闭包范围都有对大型数组的间接引用.
4: 超越DOM参考
在某些情况下,开发人员会将DOM节点存储在数据结构中,例如,当您要快速更新表中的几行时. 如果将对每个DOM行的引用存储在字典或数组中,则将有两个对同一DOM元素的引用: 一个在DOM树中,另一个在字典中. 如果不再需要这些行,则需要使这两个引用均不可访问.
var elements = {
button: document.getElementById('button'),
image: document.getElementById('image')
};
function doStuff() {
elements.image.src = 'http://example.com/image_name.png';
}
function removeImage() {
// The image is a direct child of the body element.
document.body.removeChild(document.getElementById('image'));
// At this point, we still have a reference to #button in the
//global elements object. In other words, the button element is
//still in memory and cannot be collected by the GC.
}
当涉及DOM树中的内部节点或叶节点时,还需要考虑其他因素. 如果您在代码中保留对表单元格(标签)的引用,并决定从DOM中删除该表,并且还需要保留对该特定单元格的引用,则可能会发生严重的内存泄漏. 您可能会认为垃圾收集器将释放该单元以外的所有内容,但事实并非如此. 由于单元格是表的子节点,并且该子节点保留了对父节点的引用,因此对该表单元格的引用会将整个表保存在内存中.
摘要
以上内容是对JavaScript内存管理机制的说明以及对四个常见内存泄漏的分析. 希望对JavaScript程序员有所帮助.
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/shoujiruanjian/article-312040-2.html
哎呀杨洋啊
40年
这个世界好不公平