CPython的内存管理机制

Python部落(python.freelycode.com)组织翻译,禁止转载,欢迎转发。

Python和其他高级编程语言,如Java、Ruby或JavaScript等一样,有自动内存管理机制。所以许多程序开发人员没有过多地关注内存管理,但是这可能会导致更多的内存开销和内存泄漏。这篇文章是关于CPython(Python解释器)是如何管理对象的生命周期的深度剖析,文章是我在GitHub上的 vprof 工程中记录来的,希望对大家有帮助。

引用计数

每一个Python对象都有一个引用计数器----用于记录有多少其他对象指向(引用)这个对象。它存储在变量 refcnt 中,并通过调用C宏Py_INCREF实现引用计数增加和Py_DECREF实现引用计数减少的操作。 Py_DECREF更复杂点,当引用计数器到零时,它会运行该对象的释放函数,回收该类型的对象。

通常以下两种情况你需要考虑这个宏定义:实现自己创建数据结构,或者修改已经存在的Python C API。如果你使用Python内置的数据结构,那么不需要任何操作。

如果想不增加引用计数,可以使用弱引用或 weakrefs 引用对象。 Weakrefs对于实现缓存和代理非常有用。

垃圾回收(GC)

引用计数是在Python 2.0之前管理对象生命周期的唯一方法。它有一个弱点,它不能删除循环引用的对象。 循环引用的最简单的例子是对象引用自身。

2016-11-13_143803.png

通常情况下,可以避免使用循环引用对象,但是有时是不可避免的(例如:长时间运行的程序)。

为了解决这个问题,Python 2.0引入了新的垃圾回收机制。 新GC与其他语言运行时(如JVM和CLR)的GC的主要区别在于,它仅用于寻找存在引用计数的循环引用。

循环引用只能由容器对象创建,因此Python GC不会跟踪整数,字符串等类型。

GC将对象分为3代,每一代对象都有一个计数器和一个阈值。当对象被创建时,阈值会被自动地指派为0,也就是第0代对象。当计数器大于某个阀值,GC就会运行在当前对象代上,回收该对象。没被回收的对象会被移至下一代,并且将相应的计数器复位。下一代的对象保留在下一代。

在Python 3.4之前,GC有一个致命缺点----每个对象重载了__del__()方法,因为每个对象都可以相互引用,所以GC不知道该调用那个对象的__del__()方法,这会导致GC直接跳过这些对象。具体详细信息可以参考 gc.garbage并且循环引用需要编程人员手动打破。

Python3.4介绍一种最终的解决方法finalization approach ,现在的GC可以打破对象的循环引用,而不在使用gc.garbage介绍的方法去回收对象。

此外,值得一提的是,如果你确定你的代码没有创建循环引用(或者你不关心内存管理),那么你可以只依赖引用计数器自动管理内存,而不使用GC去管理内存。

英文原文:https://medium.com/@nvdv/cpython-memory-management-479e6cd86c9#.sbvb0py87
译者:hubaoquan
 

2月15日11:00到13:00网站停机维护,13:00前恢复