在象C或C++这样的语言中,程序员负责动态地在堆中申请和释放内存。在C语言中,使用malloc() 和free()函数,在C++中,使用操作符new和delete,本质上是相同的,下面,我们仅就C语言的情况进行讨论。
由 malloc()分配的内存最终应通过明确调用free()返回到可用内存池中。在正确的时候调用free()很重要。如果内存块地址忘记了而没有调用free(),则内存一致被占用,直到程序终止。这称作’内存泄漏’(memory leak)。另一方面,如果程序调用free()释放了内存块,然后继续使用内存块,这就和另一个 malloc()调用产生了重用块的冲突。这称为’使用已释放内存’(using freed memory)。这会产生一些严重后果,如引用未初始化数据,内核转储,错误结果,莫名其妙崩溃。
一般引起内存泄漏是代码中的非普通执行路径,例如:一个函数申请了一块内存,执行一些计算,然后释放内存。现在,有一些变化,要求函数增加一些对计算的检查,以便检测错误条件,这可能使函数过早返回。当提早退出时,很容易忘记释放申请的内存块,特别在增加后面的代码时。这种泄漏,一旦引入,经常很长时间不能发现:错误出口仅发生在所有调用中的一小部分调用中,并且,现代计算机有大量的虚拟内存,所以内存泄漏只在长时间运行中,并经常使用发生泄漏的函数时,才变得明显。所以,通过编码习惯或减少这种错误的策略阻止内存泄漏的发生是很重要的。
由于Python大量使用malloc()和free(),所以需要一种策略避免内存泄漏和使用释放的内存。 Python选择的方法是引用计数(reference counting)。理论很简单:每个对象包含一个计数器,当对象的一个引用被存储到某个地方时,计数增加,当引用被删除是,计数减少,当计数器变为0,即最后一个对象的引用被删除,对象被释放。
另一个可选的策略是’自动垃圾收集’(automatic garbage collection)(有时,引用计数也被称作’垃圾收集’策略,所以这里我我使用’自动’来区别二者)。自动垃圾收集最大的优点是用户不需要明确调用free() (另一个宣称的优点是:改善速度或者内存使用-然而还没有确凿的事实)。缺点是对于C,没有确实可用方便的自动垃圾收集器,而引用计数能够方便的实现(只要提供和malloc()函数free()-这由C标准保证)。也许将来有一天为C提供了一个足够方便的自动垃圾收集器,但在那以前,我们必须依靠引用计数。
Python用传统的引用计数实现,它也提供了一个周期性的检测器,周期性地检查引用计数。这就允许应用程序不必关心直接创建或间接循环引用;这是只使用引用计数实现的垃圾收集器的弱点。引用周期由包含引用本身的对象组成,所以每个在周期中的对象有一个不为0的引用计数。典型的引用计数实现不能回收引用周期内任何对象的内存,也不能在引用周期内引用对象,即使它们在本周期内不再引用。
周期检测器能够周期地检测垃圾并能回收垃圾,只要它们最终没有在Python(__del__()方法中)中实现。当有终结器时,检测器通过垃圾收集模块(gc module)暴露检测周期(就是:模块中的garbage
变量)。 gc模块也暴露一条途径来运行检测器(collect()函数),也可配置接口,并能在运行时禁止检测器。周期检测器被作为一可选组件;虽然缺省是包括垃圾检测器的,但在Unix平台上(包括Mac OS X),可以在构造时在configure脚本中使用 --without-cycle-gc选项禁止,在其他平台上,可以通过删除’pyconfig.h’ 文件中的 WITH_CYCLE_GC
定义来禁止。如果用这种办法禁止周期检测器,gc模块将是不可用的。