【Python垃圾回收机制】-- 1565075703.2572942

目录

  • 引用计数
  • 标记-清除
  • 分代回收

原创链接: http://106.13.73.98/__/186/

Python的GC模块主要运用了 引用计数 (reference counting)来跟踪和回收垃圾。在引用计数的基础上,还可以通过 标记-清除(mark and sweep)解决容器对象可能产生的循环引用问题。通过 分代回收(generation collection)以空间换取时间来进一步提高垃圾回收的效率。

@
___

引用计数

在Python中,大多数对象的生命周期都是通过对象的引用计数来管理的。从广义上来讲,引用计数也是一种垃圾收集机制,而且也是一种最直观,最简单的垃圾收集计数。

引用计数的原理:

  1. 当一个对象的引用被创建或复制时,对象的引用计数加1;
  2. 当一个对象的引用被销毁时,对象的引用计数减1;
  3. 当一个对象的引用计数为0时,就意味着该对象不再被使用了,将释放其占用的内存。

    这里有一个类A,且看下面的操作:

    class A:
        def __init__(self):
            print('初始化完成')
    
        def __del__(self):
            print('使命完成,我去也')
    
    a1 = A()
    a2 = a1
    a3 = a1
    
    del a1
    print('删除a1')
    
    del a2
    print('删除a2')
    
    del a3
    print('删除a3')
    
    print('代码执行结束')
    
    """
    打印顺序为:
        初始化完成
        删除a1
        删除a2
        使命完成,我去也
        删除a3
        代码执行结束
    """

    ????????首先,我们创建了一个A类的对象:a1,然后通过执行赋值语句定义了a2a3,此时它们共同指向一块内存地址,专业的解释是:A类对象的引用计数为3。紧接着执行del关键字,删除对象的一个引用,当引用为0时,对象才被撤销,这就是Python垃圾回收机制之引用计数。

引用计数最大的一个优点——实时性:
????????虽然引用计数必须在每次分配和释放内存的时候加入管理引用计数的动作,然而与其它主流的垃圾收集计数相比,引用计数有一个最大的优点,即 “实时性”。任何内存,一旦没有指向它的引用,就会立即被回收。而其它的垃圾收集机制必须在满足某种特殊的条件下(必须内存分配失败)才能进行无效内存的回收。

引用计数的执行效率问题:
????????引用计数机制带来的维护引用计数的额外操作与Python运行中所进行的内存分配、释放和引用赋值的次数是成正比的。而这点相比其它主流的垃圾回收机制,比如 “标记-清除”、“停止-复制”,是一个弱点。因为这些技术所带来的额外操作基本上只是与待回收的内存数量有关。

引用计数的致命弱点——循环引用:
????????如果说执行效率还仅仅是引用计数机制的一个软肋的话,那么根不幸,引用计数机制还存在一个致命的弱点,正是因为这个弱点,使得狭义的垃圾收集从来没有将引用计数包含在内,能引发出这个致命弱点的就是循环引用(也成交叉引用)。

所谓循环引用:

????????循环引用可以使一组对象的引用计数不为0,然而这些对象实际上并没有被任何外部对象所引用,它们之间只是相互引用。这意味着不会再有人使用这组对象,应该回收这组对象所占用的内存空间,然后由于相互引用的存在,每一个对象的引用计数都不为0,因此这些对象所占用的内存永远不会被释放。

>>> a = []
>>> b = [a]
>>> a.append(b)
>>> print(a)
[[[...]]]

这一点是致命的,这与手动进行内存管理所产生的内存泄漏毫无区别。要解决这个问题,Python引入了其它的垃圾收集机制来弥补引用计数的缺陷:“标记-清除”、“分代回收”。

标记-清除

“标记-清除” 是为了解决循环引用的问题。还可以包含其它对象引用的容器对象(比如:list、set、dict、class、instance)都可能产生循环引用。

????????我们必须承认一个事实,如果两个对象的引用计数都为1,但是仅仅存在它们之间的循环引用,那么这两个对象都是需要被回收的,也就是说,它们的引用计数表现为非0,但实际上有效的引用计数为0。我们必须先将循环引用摘掉,那么这两个对象的有效计数就现身了。假设两个对象A、B,我们从A出发,因为它有一个对B的引用,则将B的引用计数减1; 然后顺着引用到达B,因为B有一个对A的引用,同样将A的引用减1;这样,就完成了对象循环引用的摘除。

????????但是这样有一个问题,假设对象A有一个对象引用C,而C没有引用A,如果将C计数减1,而最后A并没有被回收,显然,我们错误的将C的引用计数减1,这将导致未来的某个时刻出现一个对C的悬空引用。这就要求我们必须在A没有被删除的情况下复原C的引用计数,如过采用这样的方案,那么维护引用计数的复杂度将成倍增加。

标记-清除的原理:
????????“标记-清除” 采用了更好的做法,我们并不改动真实的引用计数,而是将集合中对象的引用计数复制一个副本,改动该对象引用的副本。对于副本做的任何改动,都不会影响到对象生命周期的维护。
????????这个计数副本的唯一作用是寻找 root object 集合(该集合中的对象是不能被回收的)。当成功寻找到 root object 集合之后,首先将现在的内存链表一分为二,一条链表中维护 root object 集合,成为 root 链表,而另外一条链表中维护剩下的对象,成为 unreachable 链表。之所以要剖成两个链表,是基于这样的一种考虑:现在的 unreachable 链表中的对象可能存在被 root 链表中的对象直接或间接引用,这些对象是不能被回收的,一旦在标记的过程中,发现这样的对象,就将其从 unreachable 链表移至 root 链表中;当完成标记后,unreachable 链表中剩下的所有对象就是名副其实的垃圾对象了,接下来的垃圾回收只需限制在 unreachable 链表中即可。

分代回收

????????背景:分代的垃圾收集技术是在上个世纪80年代初发展起来的一种垃圾收集机制,一系列的研究表明:无论使用何种语言开发,无论开发的是何种类型,任何规模的程序,都存在这样一点相同之处。即:一定比例的内存块的声明周期都比较短,通常是几百万条机器指令的时间,而剩下的内存块,生存周期比较长,甚至会从程序开始一直持续到程序结束。

????????从前面 “标记-清除” 这样的垃圾收集机制来看,这种垃圾收集机制带来的额外操作实际上与系统中总的内存块的数量是相关的,当需要回收的内存块数量越多时,垃圾检测带来的额外操作就越多,而垃圾回收带来的额外操作就越少;反之,当需回收的内存块越少时,垃圾检测就比垃圾回收带来更多的额外操纵。为了提高垃圾收集的效率,采用 空间换时间的策略。

分代回收原理:
????????将系统中所有内存块根据其存活时间划分为不同的集合,每一个集合就成为一个 “代”,垃圾收集的频率随着 “代” 的存活时间的增大而减小。也就是说,活的时间越长的对象,就越不可能是垃圾,就应该减少对它的垃圾收集频率。那么如何来衡量这个存活时间呢,通常是利用几次垃圾收集动作来衡量,如果一个对象经过的垃圾收集次数越多,可以得出:该对象存活时间就越长。

原文地址:https://www.cnblogs.com/gqy02/p/11309121.html

时间: 2024-10-29 02:53:11

【Python垃圾回收机制】-- 1565075703.2572942的相关文章

python垃圾回收机制详解

提到"垃圾回收机制"大家都会联想到java的垃圾回收,今天给大家讲的不是java,而是python编程语言(http://www.maiziedu.com/course/python-px/),为何会有垃圾回收机制呢?主要是为了有效的释放内存,所以python采用了一种相对简单的垃圾回收机制,下面就具体介绍python垃圾回收机制: 引用计数 Python默认的垃圾收集机制是"引用计数",每个对象维护了一个ob_ref字段.它的优点是机制简单,当新的引用 指向该对象

Python 构造函数、 Python 析构函数、Python 垃圾回收机制

构造函数与析构函数 构造函数: 用于初始化类的内容部状态,Python提供的构造函数式 __init__(); __init__()方法是可选的,如果不提供,Python 会给出默认的__init__方法 一般数据的获取需要定义的get和set方法 析构函数: 用于释放对象占用的资源,Python 提供的析构函数式__del__(); __del__()也是可选的,如果不提供,则Python 会在后台提供默认析构函数 如果要显式的调用析构函数,可以使用del关键字,方式如下: del对象名 垃圾

Python垃圾回收机制 总结

Python 垃圾回收机制 内存管理 Python中的内存管理机制的层次结构提供了4层,其中最底层则是C运行的malloc和free接口,往上的三层才是由Python实现并且维护的,第一层则是在第0层的基础之上对其提供的接口进行了统一的封装,因为每个系统都可能差异性. 内存池 Python为了避免频繁的申请和删除内存所造成系统切换于用户态和核心态的性能问题,从而引入了内存池机制,专门用来管理小内存的申请和释放.内存池分为四层:block.pool.arena和内存池.如下图: block:有很多

【Python垃圾回收机制】 -- 2019-08-08 20:38:58

目录 引用计数 标记-清除 分代回收 原文: http://106.13.73.98/__/186/ Python的GC模块主要运用了 引用计数 (reference counting)来跟踪和回收垃圾.在引用计数的基础上,还可以通过 标记-清除(mark and sweep)解决容器对象可能产生的循环引用问题.通过 分代回收(generation collection)以空间换取时间来进一步提高垃圾回收的效率. @ ___ 引用计数 在Python中,大多数对象的生命周期都是通过对象的引用计数

【Python垃圾回收机制】 -- 2019-08-09 10:26:20

目录 引用计数 标记-清除 分代回收 原文: http://106.13.73.98/__/186/ Python的GC模块主要运用了 引用计数 (reference counting)来跟踪和回收垃圾.在引用计数的基础上,还可以通过 标记-清除(mark and sweep)解决容器对象可能产生的循环引用问题.通过 分代回收(generation collection)以空间换取时间来进一步提高垃圾回收的效率. @ ___ 引用计数 在Python中,大多数对象的生命周期都是通过对象的引用计数

python垃圾回收机制的一些理解

概览: 主要通过 引用计数来进行垃圾收集, 就是说,当一个对象没有被其他对象引用的时候,会释放掉内存. 但是会有一些循环引用的对象,通过上面的方法,是没有办法清除掉的.所以,python还有另外的一个机制来解决这个问题,那就是标记-清除. 标记-清除: 主要过程为, 扫描所有容器对象(不会扫描int, string,这些简单对象,因为他们不能包含其他对象的引用,不会造成循环引用),通过一种方法将这些对象分为两部分,一部分表示可以被删除,一部分表示不可被删除,然后将可以被删除的对象回收掉. 那么首

python 垃圾回收机制

Python的GC模块主要运用了"引用计数"(reference counting)来跟踪和回收垃圾.在引用计数的基础上,还可以通过"标记-清除"(mark and sweep)解决容器对象可能产生的循环引用的问题.通过"分代回收"(generation collection)以空间换取时间来进一步提高垃圾回收的效率. 一.引用计数 在Python中,大多数对象的生命周期都是通过对象的引用计数来管理的.从广义上来讲,引用计数也是一种垃圾收集机制,

Python垃圾回收机制:gc模块

在Python中,为了解决内存泄露问题,采用了对象引用计数,并基于引用计数实现自动垃圾回收. 由于Python 有了自动垃圾回收功能,就造成了不少初学者误认为不必再受内存泄漏的骚扰了.但如果仔细查看一下Python文档对 __del__() 函数的描述,就知道这种好日子里也是有阴云的.下面摘抄一点文档内容如下: Some common situations that may prevent the reference count of an object from going to zero i

Python垃圾回收机制--完美讲解!

转自: http://www.jianshu.com/p/1e375fb40506 先来个概述,第二部分的画述才是厉害的. Garbage collection(GC) 现在的高级语言如java,c#等,都采用了垃圾收集机制,而不再是c,c++里用户自己管理维护内存的方式.自己管理内存极其自由,可以任意申请内存,但如同一把双刃剑,为大量内存泄露,悬空指针等bug埋下隐患.对于一个字符串.列表.类甚至数值都是对象,且定位简单易用的语言,自然不会让用户去处理如何分配回收内存的问题.python里也同