Open ls0f opened 8 years ago
Python中的内存管理方法之一就是引用计数。引用计数非常高效,实现简单。对象被引用时其引用计数加1, 当其不再被一个变量引用时则计数减1. 当引用计数等于0时对象被删除。 比如下面的代码:
引用计数
def test(): l = [] l.append(0)
test函数调用结束后,l对象引用计数减1变为0,会被自动删除。 但引用计数的一个大问题就是不能解决循环引用,比如看下面的代码:
l
class A: pass def test(): a = A() a.cycle = a
a循环引用自身,因此函数退出后引用计数不为0而是1,对于这样的对象,只能通过其他GC手动来标记并清除。
Python中有gc模块,垃圾回收是自动进行的,你可以通过gc.disable()来禁用自动垃圾回收,手动调用gc.collect()来进行垃圾回收。
gc
gc.disable()
gc.collect()
gc是个昂贵且费力的事情。垃圾回收时,Python不能进行其它的任务。所以,Python只会在特定条件下,自动启动垃圾回收。当Python运行时,会记录其中分配对象(object allocation)和取消分配对象(object deallocation)的次数。当两者的差值高于某个阈值时,垃圾回收才会启动。 可以通过gc模块的get_threshold()方法,查看该阈值。
get_threshold()
In [1]: import gc In [2]: gc.get_threshold() Out[2]: (700, 10, 10)
700即是垃圾回收启动的阈值,后面的两个10是与分代回收相关的阈值。
Python同时采用了分代(generation)回收的策略。这一策略的假设:存活时间越久的对象,越不可能在后面的程序中变成垃圾。我们的程序往往会产生大量的对象,许多对象很快产生和消失,但也有一些对象长期被使用。出于信任和效率,对于这样一些“长寿”对象,我们相信它们的用处,所以减少在垃圾回收中扫描它们的频率。
Python将所有的对象分为0,1,2三代。所有的新建对象都是0代对象。当某一代对象经历过垃圾回收,依然存活,那么它就被归入下一代对象。垃圾回收启动时,一定会扫描所有的0代对象。如果0代经过一定次数垃圾回收,那么就启动对0代和1代的扫描清理。当1代也经历了一定次数的垃圾回收后,那么会启动对0,1,2,即对所有对象进行扫描。
这两个次数即上面get_threshold()返回的(700, 10, 10)返回的两个10。也就是说,每10次0代垃圾回收,会配合1次1代的垃圾回收;而每10次1代的垃圾回收,才会有1次的2代垃圾回收。
可以用set_threshold()来调整,比如对2代对象进行更频繁的扫描。
import gc gc.set_threshold(700, 10, 5)
Python使用标记清除来处理循环引用问题。可以包含其他对象引用的容器对象(如list, dict, set,甚至class)都可能产生循环引用。Python复制每个容器的引用计数,可以记为gc_ref。然后遍历容器列表,对每个容器引用的对象j,对j的gc_ref减1。在结束遍历后,gc_ref不为0的对象,和这些对象引用的对象,以及继续更下游引用的对象,需要被保留,而其它的对象则被垃圾回收。
标记清除
参考: http://www.cnblogs.com/vamei/p/3232088.html http://hbprotoss.github.io/posts/pythonla-ji-hui-shou-ji-zhi.html http://stackoverflow.com/questions/4484167/python-garbage-collector-documentation
Python中的内存管理方法之一就是
引用计数
。引用计数非常高效,实现简单。对象被引用时其引用计数加1, 当其不再被一个变量引用时则计数减1. 当引用计数等于0时对象被删除。 比如下面的代码:test函数调用结束后,
l
对象引用计数减1变为0,会被自动删除。 但引用计数的一个大问题就是不能解决循环引用,比如看下面的代码:a循环引用自身,因此函数退出后引用计数不为0而是1,对于这样的对象,只能通过其他GC手动来标记并清除。
Python中有
gc
模块,垃圾回收是自动进行的,你可以通过gc.disable()
来禁用自动垃圾回收,手动调用gc.collect()
来进行垃圾回收。gc是个昂贵且费力的事情。垃圾回收时,Python不能进行其它的任务。所以,Python只会在特定条件下,自动启动垃圾回收。当Python运行时,会记录其中分配对象(object allocation)和取消分配对象(object deallocation)的次数。当两者的差值高于某个阈值时,垃圾回收才会启动。 可以通过gc模块的
get_threshold()
方法,查看该阈值。700即是垃圾回收启动的阈值,后面的两个10是与分代回收相关的阈值。
Python同时采用了分代(generation)回收的策略。这一策略的假设:存活时间越久的对象,越不可能在后面的程序中变成垃圾。我们的程序往往会产生大量的对象,许多对象很快产生和消失,但也有一些对象长期被使用。出于信任和效率,对于这样一些“长寿”对象,我们相信它们的用处,所以减少在垃圾回收中扫描它们的频率。
Python将所有的对象分为0,1,2三代。所有的新建对象都是0代对象。当某一代对象经历过垃圾回收,依然存活,那么它就被归入下一代对象。垃圾回收启动时,一定会扫描所有的0代对象。如果0代经过一定次数垃圾回收,那么就启动对0代和1代的扫描清理。当1代也经历了一定次数的垃圾回收后,那么会启动对0,1,2,即对所有对象进行扫描。
这两个次数即上面get_threshold()返回的(700, 10, 10)返回的两个10。也就是说,每10次0代垃圾回收,会配合1次1代的垃圾回收;而每10次1代的垃圾回收,才会有1次的2代垃圾回收。
可以用set_threshold()来调整,比如对2代对象进行更频繁的扫描。
Python使用
标记清除
来处理循环引用问题。可以包含其他对象引用的容器对象(如list, dict, set,甚至class)都可能产生循环引用。Python复制每个容器的引用计数,可以记为gc_ref。然后遍历容器列表,对每个容器引用的对象j,对j的gc_ref减1。在结束遍历后,gc_ref不为0的对象,和这些对象引用的对象,以及继续更下游引用的对象,需要被保留,而其它的对象则被垃圾回收。参考: http://www.cnblogs.com/vamei/p/3232088.html http://hbprotoss.github.io/posts/pythonla-ji-hui-shou-ji-zhi.html http://stackoverflow.com/questions/4484167/python-garbage-collector-documentation