Python的垃圾回收机制

来源:互联网 发布:淘宝无线店铺新品上架 编辑:程序博客网 时间:2024/05/16 01:36

1.1    Python的垃圾回收机制

很多文献没有对Python的垃圾回收进行非常详细的描述,这里将重点介绍一下Python的垃圾回收机制。Python采用自动和手动相结合的垃圾回收方法,其原理基于引用计数和分代垃圾回收以及标记-清除。
通常情况下,Python会使用引用计数的方式回收垃圾,即任何一个对象的引用计数为0的时候就立刻回收这个对象占有的内存。众所周知,引用计数的方法无法回收存在循环引用的容器对象,而非容器对象不可能存在循环引用。于是Python就定义了一个模块gc专门处理相类似的情况,在这个模块中补充以标记-清除以及分代垃圾回收。gc里面有一个可供用户手动调用的collect()函数,用户可以自己导入这个模块,并调用gc.collect()手动回收。即使用户不手动回收,当容器对象的数目达到一个阈值的时候也会触发collect()函数。具体的过程如下:
Python将所有的容器对象分成3代,每一个新申请的容器对象都会插入到第0代中,如果第0代中对象的个数超过一个预先设定的阈值就会自动启动分代垃圾回收,他先将比当前回收的代更年轻的所有代都链接到当前代的链表中。然后在当前这个链表上使用标记算法,即把当前代的成员分成两部分:一部分是从根集合以及从根集合出发可以到达的对象,另一部分是不可达的对象。
经过上面的操作后得到的所有不可达的对象还不能立即进行回收,当一个类中用户自定义__del__属性时,垃圾回收机制会把此类的对象的垃圾回收工作交给用户。如果两个类的对象都定义了__del__函数,并且存在着循环引用时可能会导致当前被回收对象引用一个已经被删除的对象。Python的垃圾回收机制会把具有这种情况的对象移动到一个成为finalizer的链表中,最后将这个链表与比当前代更长的代合并。

最后Python会对弱引用的情况,当一个弱引用指向一个对象的时候并不会导致这个对象的引用计数增加。当删除一个被多个弱引用指向对象时,会触发一个事先定义好的回调函数,Python会按照从最新定义的弱引用到最早定义的弱引用的顺序依次调用其相对应的回调函数。为了防止与具有__del__属性的对象的相似的情况,如果弱引用对象也位于这个不可达则全部当成已经被回收而不去调用其相应的回调函数。整个Python的垃圾回收过程如下图所示:


2.    容器对象管理

Python为内存申请提供了三个接口,主要是:

PyMem_MALLOC(size_t n);//对malloc的简单封装

PyMem_New(type,n);//给PyMem_MALLOC(size_t n)加上类型信息

PyObject_Malloc(size_t nbytes);//用于申请小于256字节的内存

PyMem_MALLOC(size_t n)是对申请0字节内存的简单处理,因为某些平台下malloc(0)返回的是NULL,而某些平台下返回的是一个貌似正常的指针;PyMem_New(type,n)在函数PyMem_MALLOC(size_t n)的基础上封装以类型信息;PyObject_Malloc(size_t nbytes)申请的内存处于Python的内存池管理之下。

2.1      小块内存的内存池模型
所谓小块内存是指不超过256B的内存块,小块内存的管理使用三层结构,分别为arena,pool,block。arena起到的是组织所有已经申请小块内存的作用。一个pool通常是一个页的大小, block的大小为一些离散的值:8, 16, 24 ,…,256。block是内存分配的基本单元,所有小块内存的申请会被自动对齐到这些离散的值。arena的作用主要是把所有的pool管理起来,方便后面的内存释放操作。内存都是按需分配的,Python虚拟机默认为每一个arena分配256KB的内存,一个pool的大小为4KB。一个pool只能存储值相同的block。也就是说所有的容器对象都是放在这个内存池中,比如:list,tuple,dict等等。三者之间的关系在《Python源码剖析》中有很详细的描述


总结一下:
Python的垃圾回收基于引用计数,只要某个对象的引用计数为0就回收它。对于那些产生了循环引用的,有一个专门的模块叫做gc,来处理。注意了,产生循环引用的只有可能是容器对象,因为非容器对象不可能有指针指向其他的对象。如果你的循环引用的对象太多,超过了一个阈值,就会触发gc。当然这个gc做的工作不光光是解决循环引用了,还有其他一些杂七杂八的。大家在写Python程序的时候要注意不要产生循环引用,调用这个gc模块会影响程序性能。
原创粉丝点击