到底什么是内存泄漏

内存生命周期

不管什么程序语言,内存生命周期是一致的:

  1. 分配内存(allocate);
  2. 使用分配到的内存(read,write);
  3. 不需要时,归还、释放(release);
    所有语言第二部分都是明确的。第一和第三部分在底层语言中是明确的,但在像JavaScript这些高级语言中,大部分都是隐含的。

垃圾收集(garbage collection)

引用(reference)

垃圾收集算法主要依赖于“引用”概念。
在内存管理的环境中,一个对象如果有访问另一个对象的权限(隐式或者显式),叫做一个对象引用另一个对象。例如,一个Javascript对象具有对它原型的引用(隐式引用)和对它属性的引用(显式引用)。
在这里,“对象”的概念不仅特指 JavaScript 对象,还包括函数作用域(或者全局词法作用域)。

引用计数垃圾回收(reference-counting garbage collection)

这是最天真的垃圾收集算法。此算法把“对象是否不再需要”简化定义为“对象有没有其他对象引用到它”。如果没有引用指向该对象(零引用),对象将被垃圾回收机制回收。

Limitation: cycles

下面的例子中,两个对象被创建,并且相互引用,形成了一个cycle。
它们在函数调用后离开作用域,所以它们是没用的了,可以被回收了。但是引用计数算法考虑到这俩对象依然被至少引用了一次,所以不会垃圾收集它们。

1
2
3
4
5
6
7
8
function f(){
var o = {};
var o2 = {};
o.a = o2; //o 引用 o2
o2.a = o; // o2 引用 o
return "june is a cute girl";
}
f();

标记清除算法(mark-and-sweep algorithm)

这个算法把“对象是否不再需要”简化定义为“对象是否可以获得”。
这个算法假定设置一个叫做根(root)的对象(在Javascript里,根是全局对象)。垃圾回收器将定期从根开始,找所有从根开始引用的对象,然后找这些对象引用的对象……从根开始,垃圾回收器将找到所有可以获得的对象和收集所有不能获得的对象。

这个算法比前一个要好,因为“有零引用的对象”总是不可获得的,但相反却不一定,参考“循环引用”。
从2012年起,所有现代浏览器都使用了标记-清除垃圾回收算法。

Cycles不再是问题

在上面的示例中,函数调用返回之后,两个对象从全局对象出发无法获取。因此,他们将会被垃圾回收器回收。

什么是内存泄露?

内存泄漏本质上是指应用不再需要的内存没有返还给操作系统或者闲置内存池。然而,某内存是否有用是一个难以判断的问题。只有程序员才知道一个内存是否要返还给操作系统。

参考链接: