python 入门书籍,python3入门书籍

  python 入门书籍,python3入门书籍

  Python的垃圾收集以引用计数为主,标记清除和代收集为辅。因为引用计数最大的缺陷是循环引用,所以Python采用了辅助的方法。本文没有深入研究Python垃圾收集机制的内部实现,而是以gc模块为切入点来学习Python垃圾收集机制。如果想了解更多,请阅读Python的源代码进行分析。

  请看下面的代码:

  importgcimportsysgc . set _ debug(GC . debug _ stats GC . debug _ leak)a=[]b=[]a . append(b)b]print arak

  输出结果:

  a refcount 33602 bref计数:3 GC:收集生成2。GC:objectsineachgeneration 3360005131 GC 3360完成,耗时0.0020秒。GC:objectsineachgeneration 336005125 GC:done,用了0.0010秒。

  可见垃圾回收是行不通的,所以垃圾回收只能起到循环引用的作用。

  你可能会好奇,为什么A的引用数是2?此时,是否需要看到sys.getrefcount(object)的函数描述?

  啊,在这个函数Docstring中,返回值通常比我们预期的多1。这是因为引用被添加到再次传递给函数的参数的临时变量中。是的,但奇怪的是,为什么不调整它?

  Gc.collect()返回垃圾收集中不可到达的)对象的数量。什么是不可及的物体?请看下面的代码:

  append(a)a)del Adel b print GC . collect .

  输出结果:

  Gc:收集第二代。GC:objectsineach generation 3360405127 GC:collectable list 02648918 GC 3360 collectable le list 000 0不可收集,0.0030s elapsed.2这次A和B是循环引用,垃圾回收还在发挥作用。两个回收列表的对象是A和B,我不相信可以用:hex(id(a))。

  上面收集的对象都是不可及的对象,但是不可及的对象呢?要描述不可及的对象,必须知道Python标记-垃圾收集机制。简而言之,过程如下。

  *找到根对象集合。根对象通常指全局引用和函数栈上的引用,其中a是根对象,如上面的代码所示。

  从根对象开始并通过每个引用到达的所有对象都被标记为“可到达”。

  删除除可达(垃圾收集)()))))))))))))之外的所有对象。

  这里还有必要提一下垃圾回收——可回收对象链表。连接Python链表中所有可能产生循环引用的对象。可能产生循环引用的对象是容器类,比如list、dict和class,而int和string不是。每次实例化这个对象时,它都会被添加到这个链表中。这个链表被称为可收集对象的链表(

  比如a=[],b=[],c={},就会生成一个header-a-b-c双向链表。

  考虑上面代码的垃圾收集过程。当调用gc.collect()时,根对象将开始垃圾收集。del a和del b后,A和B成为不可达对象,循环引用被删除,所以所有的A和B引用都会被0,A和B回收。

  看看下面的代码,加深上面的理解。

  A=[] b=[] a .追加(b) b .追加(a) a) del b print gc.collect)):

  Gc:收集第二代。GC:objectsineachenervation 336035447710 GC:done,用了0.0010秒。GC:objectsineachenervation 336005119 GC:完成,耗时0.0020秒。这次不会。

  有垃圾收集。虽然del b没了,但是从A开始,找到了对B的引用,所以B还是一个可达对象,所以不会被收集。

  Python的垃圾收集机制是否意味着不会造成内存泄漏?不,请看下面的代码:

  class a:def _ _ del _ _(self):pass class b:def _ _ del _ _(self):passa=a()b=b()print hex(id(a))print hex(id(a . _ _ dict _ _))a . b=bb

  0x25cff300x25d0b70gc:收集第2代.gc:每代中的对象:364 4771 0gc:025 CFF 30 GC的不可收集A实例:025CFF58gc的不可收集B实例:025D0B70gc:不可收集dict 025D0810gc: done,4 unreachable,4 uncollectable,0.0020s elapsed.4[__main__。0x025CFF30处的实例,__main__。0x025CFF58处的b实例,{b: __main__。0x025CFF58}处的b实例,{a: _ _ main _ _。0x025cff30}] GC处的实例:收集第2代.GC:每代中的对象:20 5127 GC: done,用了0.0010s。从输出中,我们可以看到单词不可收集。很明显,这个垃圾收集做不到,导致内存泄漏。

  为什么会这样?因为在使用del b的时候,会调用B的__del__方法,这个方法很可能会用到B.A,但是如果A在之前的del a中被回收,就会引起异常。所以Python没办法,造成无法收集,也导致内存泄露。因此,应该谨慎使用__del__方法。如果使用,必须保证没有循环引用。

  我们也打印出了A的地址,上面打印十六进制(id(a)),也验证了确实是回收的A。

  GC。垃圾出现在上面,GC返回的对象。垃圾是不可及的对象,不能回收。仔细看看输出结果。为什么它们看起来是重复的?这个困扰了我很久,直到打开gc模块的文档才明白。由于我们之前的GC . set _ debug(GC . debug _ stats GC . debug _ leak),以及GC . debug _ leak=GC . set _ debug(GC . debug _ stats GC . debug _ collectible GC . debug _ un collectible GC . debug _ instances GC . debug _ objects GC . debug _ save all)在文档中指出,如果GC。设置了DEBUG_SAVEALL,所有无法访问的对象都将被添加到gc.garbage返回的列表中,而不仅仅是那些无法回收的对象。

  我们来看看Python的分代收集机制。

  Python有三个“代”。所谓三个‘代’,就是三个链表,也就是上面说的可收藏对象的链表。当每一代中的对象数量达到一定数量时,将触发Python垃圾收集。每一代的数量如下。

  按代收集的思想是,物体寿命越长,垃圾就越少,回收的频率应该越低。所以当Python发现这个对象在几次垃圾收集后是可达的,就把它移到第二代,以此类推。那么Python是如何检查每一代是否达到阈值的呢?Python从第三代开始每次都会检查。如果第三代中的对象大于阈值,则将同时收集第三代、第二代和第一代的对象。第二代满足了,第二代和第一代里面的物件就回收了,设计的太漂亮了。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: