内存泄露是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢系统崩溃等严重后果
内存泄露缺陷具有隐蔽性,积累性的特征,比其他内存非法访问错误更难检测,因为内存泄露额产生原因是内存块未释放,属于遗漏型缺陷而不是过错型缺陷。逐渐积累,降低系统整体性能,极端的情况下会导致系统崩溃
无论是C还是C++程序,运行时候的变量主要有三种分配方式:堆分配、栈分配、全局和静态内存分配。内存泄漏主要是发生在堆内存分配方式中,即“配置了内存后,所有指向该内存的指针都遗失了”,若缺乏语言这样的垃圾回收机制,这样的内存片就无法归还系统。因为内存泄漏属于程序运行中的问题,无法通过编译识别,所以只能在程序运行过程中来判别和诊断。下面将介绍几种常用的内存检测方法,每种方法均以现有的内存检测工具为分析范例,并对各种方法进行比较。
Javascript 的内存管理
Javascript 是那些被称作垃圾回收语言当中的一员。垃圾回收语言通过周期性地检查那些之前被分配出去的内存是否可以从应用的其他部分访问来帮助开发者管理内存。换句话说,垃圾回收语言将内存管理的问题从“什么样的内存是仍然被使用的?”简化成为“什么样的内存仍然可以从应用程序的其他部分访问?”。两者的区别是细微的,但是很重要:开发者只需要知道一块已分配的内存是否会在将来被使用,而不可访问的内存可以通过算法确定并标记以便返还给操作系统。
3 种常见的 Javascript 内存泄露
第一种意外的全局变量
一个未声明变量的引用会在全局对象中创建一个新的变量,就是window对象
尽管我们在讨论那些隐蔽的全局变量,但是也有很多代码被明确的全局变量污染的情况。按照定义来讲,这些都是不会被回收的变量(除非设置 null 或者被重新赋值)。特别需要注意的是那些被用来临时存储和处理一些大量的信息的全局变量。如果你必须使用全局变量来存储很多的数据,请确保在使用过后将它设置为 null 或者将它重新赋值。常见的和全局变量相关的引发内存消耗增长的原因就是缓存。缓存存储着可复用的数据。为了让这种做法更高效,必须为缓存的容量规定一个上界。由于缓存不能被及时回收的缘故,缓存无限制地增长会导致很高的内存消耗。
第二种被遗漏的定时器和回调函数
var element = document.getElementById('button');
function onClick(event) {
element.innerHtml = 'text';
}
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.
第三种DOM 之外的引用
有些情况下将 DOM 结点存储到数据结构中会十分有用。假设你想要快速地更新一个表格中的几行,如果你把每一行的引用都存储在一个字典或者数组里面会起到很大作用。如果你这么做了,程序中将会保留同一个结点的两个引用:一个引用存在于 DOM 树中,另一个被保留在字典中。如果在未来的某个时刻你决定要将这些行移除,则需要将所有的引用清除。
ar 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);
// Much more logic
}
function removeButton() {
// The button is a direct child of body.
document.body.removeChild(document.getElementById('button'));
// At this point, we still have a reference to #button in the global
// elements dictionary. In other words, the button element is still in
// memory and cannot be collected by the GC.
}
闭包
JavaScript 开发中一个重要的内容就是闭包,它是可以获取父级作用域的匿名函数。Meteor 的开发者发现在一种特殊情况下有可能会以一种很微妙的方式产生内存泄漏,这取决于 JavaScript 运行时的实现细节。
var theThing = null;
var replaceThing = function () {
var originalThing = theThing;
var unused = function () {
if (originalThing)
console.log("hi");
};
theThing = {
longStr: new Array(1000000).join('*'),
someMethod: function () {
console.log(someMessage);
}
};
};
setInterval(replaceThing, 1000);