zoukankan      html  css  js  c++  java
  • 谈一谈python的垃圾回收机制

    python的垃圾回收机制是怎么实现的

      在C语言时代程序员要负责内存的申请和释放,虽然这样的程序可以对资源进行精细的控制、但是它也有它的问题、这就要求程序员

      要写许多与业务逻辑无关的内容在代码里面;更大的问题是程序员有可能忘记了释放自己申请的资源。如果一个程序总是申请资源而

      不主动把资源释放给操作系统、那么操作系统有再多的资源也不够给程序败的。

      所以呢! C语言不但对程序员的能力有比较高的要求,还要它比较细心才行;我之前就遇到过一个家公司他们的C#代码有问题,Task

      对象应该在用完之后要close的、可是程序员并没有这样做,这使得主机运行几天内存就不足了;然后他们就每天凌晨没人用的时候重

      启一下window-server。  

      与大多数语言一样python的垃圾的回收也是基于引用计数来实现的;也就是说你用python创建的每一个对象python解释器都会在后台

      默默的给你记住有多少个变量名引用了你创建的对象、当它发现有0个变量名引用了你创建的对象之后、解释器就会把对象给释放掉。

    1、如果确定一个对象有多少个变量名引用了它

      sys模块的getrefcount函数可以返回给定对象的引用数量

    >>> i = 65535
    >>> sys.getrefcount(i)
    2
    >>> 

      为什么返回的是2而不是 1 、根据官方文档的说法是getrefcount函数的内部也引用传入的对象、所以i对象在两处被引用了、所以返回

      了2。值得一提的是,这还并不是关于gerefcount的全部,请看下面的代码

    >>> j = 1
    >>> sys.getrefcount(j)
    733

      这时返回733的原因是由于官方对性能的一个优化造成的,像“1”这样的值是非常、常用的,所以python并没有为这733处用到1的地方都

      创建一个“1”而是让他们共用同一个“1”所以对于一些比较特别的getrefcount的返回看起一就大了去了。

    2、什么是弱引用

      要说清楚弱引用还要通过一般的引用说起、如果你有其它oop语言的经历你可能会知道“装箱”,“拆箱”这一对名词,在python中一些都

      是对象并没有谁是特殊的,也就是说不存在“装箱”,“拆箱”的概念;对于赋值语句最正确的理解是在对象上粘标签,用下面例子来说明

      这个

    >>> i = 65535
    >>> j = i
    >>> 

      第一行说的是创建一个对象(int类型)它的值是65535、并给对象粘上标签“i”、在这之后就可以通过“i”引用65535这个值了;

      第二行是在i所引用的对象上再粘一个标签,这个新标签的名字叫“j”;应该和你想的一样由于65535这个对象分别被“i,j”引用

      了,所以sys.getrefcount(i)应该要返回“3”

    >>> i = 65535
    >>> j = i
    >>> sys.getrefcount(j)
    3

       弱引用就是虽然在对象上多粘了一个标签,但是对象的引用计数器并不会增长。感觉文字不足以表达,还是直接上代码吧;

    >>> from weakref import ref
    >>> class Node(object):
    ...     pass
    ... 
    >>> a = Node()
    >>> sys.getrefcount(a)
    2
    >>> b = a
    >>> sys.getrefcount(a)
    3
    >>> c = ref(a)
    >>> sys.getrefcount(a)
    3

      可以看到通过ref函数转一道手后 c 这个标签虽然粘上去了,但是并不会引起引用计数器的增长。

    3、我们通过什么方式可以观察到垃圾回收

      对象的__del__这个魔术方法就是在垃圾回收时调用的,也就是说我们只要重写这个方法、并在方法内打印一定的输出我们就可以做到

      垃圾回收的肉眼可见了。

    >>> class Node():
    ...     def __init__(self,value):
    ...         self.value=value
    ...     def __del__(self):
    ...         print('this is __del__'.format(self.value))
    ... 
    >>> n = Node(123)
    >>> del n
    this is __del__
    
    >>> n = Node(456)
    >>> n = None
    this is __del__

      可以看到有两种方法可以触发垃圾回收程序、1):显示的del一个对象,2):当一个对象的引用计数器为零时 。这两种情况任何一种发

      生后对象就会被标记为“可回收”状态,垃圾回收器会基于一定的算法对它们进行垃圾回收。

    4、谈谈垃圾回收不起作用的情况

      正常情况下没有垃圾回收不起作用的情况、通常垃圾得不到回收是因为我们的代码逻辑上有问题;这个问题使得,即使对于那些

      不使用的对象它的引用计数器也不为 0 ,这就使得垃圾回收不起作用了。这种问题目前我遇到过的情况就是因为“环形”的数据

      结构引起的、可以看一下的例子

    >>> class Node(object):
    ...     def __init__(self,value,child=None):
    ...         self.value=value
    ...         self.child=child
    ...     def set_child(self,child):
    ...         self.child=child
    ...     def __del__(self):
    ...         print("Node.__del__ {0}".format(self.value))
    
    >>> child = Node('child')
    >>> root.set_child(child)
    >>> child.set_child(root)
    >>> del child
    >>> del root
    >>> 
    >>> 
    >>>

      可以看到 del child 和 del root 都没有能触发垃圾回收、问题就在于对象的引用计数器并不为零;拿root来说吧,它还被child.child这个名字引用着

      所以引用计数器并不认为child是一个可以回收的对象,所以我们并没有看到理想中的__del__魔术方法的执行。  

    5、通过弱引用来修复bug

    from time import sleep
    from weakref import ref
    from sys import getrefcount
    
    class Node(object):
    ...     def __init__(self,value,child=None):
    ...         self.value=value
    ...         self.child=child
    ...     def set_child(self,child):
    ...         self.child=ref(child) #self.child以弱引用方式实现
    ...     def __del__(self):
    ...         print("Node.__del__ {0}".format(self.value))
    
    >>> root = Node('root')
    >>> child = Node('child')
    >>> root.set_child(child)
    >>> child.set_child(root)
    >>> print(getrefcount(root))
    2
    >>> print(getrefcount(child))
    2
    >>> del child
    Node.__del__ child
    >>> del root
    Node.__del__ root
    >>> 
    >>> 
    >>> 

          

      

      

     

    ----

  • 相关阅读:
    12.12
    12.11
    1208
    1206
    2018-12-23丛晓强作业
    2018-12-17面向对象总结
    2018-12-17-丛晓强作业
    2018-12-13丛晓强作业
    2018-12-12丛晓强作业
    2018-12-11丛晓强作业
  • 原文地址:https://www.cnblogs.com/JiangLe/p/9183038.html
Copyright © 2011-2022 走看看