标准python垃圾回收器由两部分组成,即引用计数回收器和分代垃圾回收器(即python包中的gc module)。其中,引用计数模块不能被禁用,而GC模块可以被禁用。

引用计数算法

python中每个变量都是对象,每个对象核心都包含一个结构体PyObject:

 typedef struct_object {
int ob_refcnt;
struct_typeobject *ob_type;
} PyObject;

其中ob_refcnt变量记录了这个对象被引用的数量,当这个对象的引用被赋值给另一个变量或者这个引用被删除(如del a)。那么ob_refcnt就会随之加1或者减1。引用计数增加的场景有:

  • 赋值操作
  • 参数传递
  • 添加object到list末尾

如果某个对象的引用计数为0,那么python会立马调用函数释放内存空间,此时可能导致这个对象引用的其他对象引用计数也为0,因此一个对象消亡可能引起一连串对象内存空间被释放。

一般说来,全局变量(定义在函数,类和代码块之外的变量)随python进程退出而释放,因此引用计数永远不会减为0。局部变量(定义在函数,类和代码块之内的变量)反之。

引用计数算法原理简单明了,但是存在循环引用,线程锁和内存性能开销等问题。

分代垃圾回收算法

引用计数算法最大问题就是循环引用,这种情况下循环引用的每个对象的引用计数最终都不会归零,如下图所示:



因此,python在gc module实现了循环检测算法来解决这个问题。循环引用只可能发生在容器对象之间,因此,GC循环检测也只对容器对象使用。对于只包含常量的容器(如包含常量的tuple),GC也会把这部分排除掉。

GC将容器对象分为3代,定期清理每代的垃圾对象。新分配的对象总是在第0代,如果经历过一次垃圾回收依然存活则移入下一代。新生代的回收频率比老年代高,大部分新对象很快就会释放,因此生命周期都只是停留在第0代。这种分代回收方式可以提高GC性能,减少停顿时间。

GC进行时需要停止程序的执行以免引用关系改变。

为了确定何时可以执行垃圾回收,GC每一代维护都会count和threshold两个变量,其中count=上一次垃圾回收后分配的新对象-上一次垃圾回收后已经消亡对象(因为引用计数为0而被释放),每次对象创建时候都会比较count和threshold大小,如果count>threshold,python就会启动垃圾回收进程。

以下详细说明CPython循环检测的原理,CPython维护两个双向链表:一个是需要检测的对象关系链表(objects list),一个是存放无法访达的对象链表(Unreachable list)。

  1. 如下图所示,假设现在清理的是第0代的垃圾对象。左边的需要扫描的对象列表中link4是一个循环引用。varA是link1的外部引用,可能是其他代的对象。每个对象除了有一个引用计数变量ref count外,还维护一个gc_ref变量,初始时ref count=gc_ref.

  2. GC遍历objects list每个对象,将这个对象所引用的其他对象的gc_ref减1。
  3. GC将步骤2中gc_ref=0的对象挪到Unreachable list中,下图表示将已经处理的link3和link4移到Unreachable list,但是还没有处理link1和link2.
  4. 当GC扫描link1时候,发现gc_ref=1,是可以访达的,那么GC就会循环遍历link1引用的对象,将其标记为可以访达。因为link3经过此轮后标记为可以访达,那么就会将其从Unreachable list移到之前的list中。
  5. GC扫描完所有的对象后,就会启动垃圾回收进程,将Unreachable list中的对象清理掉。

为了性能考虑,我们应该尽量避免循环引用,可以使用weakrefmodule,weakref.ref不会增加对象的引用计数并且对象被回收后返回None。此外,可以使用gc.disable()手动关闭GC垃圾回收,这在某些特殊情况下有帮助。

参考资料

[1] Garbage collection in Python: things you need to know

[2] Python垃圾回收机制

[3] The Garbage Collector

[4] Python垃圾回收(GC)三层心法,你了解到第几层

python垃圾回收算法的更多相关文章

  1. 《垃圾回收的算法与实现》——Python垃圾回收

    Python垃圾回收 python采用引用计数法进行垃圾回收 Python内存分配 python在分配内存空间时,在malloc之上堆放了3个独立的分层. python内存分配时主要由arena.po ...

  2. Python垃圾回收机制

    引用计数Python默认的垃圾收集机制是“引用计数”,每个对象维护了一个ob_ref字段.它的优点是机制简单,当新的引用指向该对象时,引用计数 引用计数 Python默认的垃圾收集机制是“引用计数”, ...

  3. 深入理解java虚拟机【垃圾回收算法】

    Java虚拟机的内存区域中,程序计数器.虚拟机栈和本地方法栈三个区域是线程私有的,随线程生而生,随线程灭而灭:栈中的栈帧随着方法的进入和退出而进行入栈和出栈操作,每个栈帧中分配多少内存基本上是在类结构 ...

  4. (转)《深入理解java虚拟机》学习笔记3——垃圾回收算法

    Java虚拟机的内存区域中,程序计数器.虚拟机栈和本地方法栈三个区域是线程私有的,随线程生而生,随线程灭而灭:栈中的栈帧随着方法的进入和退出而进行入栈和出栈操作,每个栈帧中分配多少内存基本上是在类结构 ...

  5. C/C++中几种经典的垃圾回收算法

    1.引用计数算法 引用计数(Reference Counting)算法是每个对象计算指向它的指针的数量,当有一个指针指向自己时计数值加1:当删除一个指向自己的指针时,计数值减1,如果计数值减为0,说明 ...

  6. 深入理解java垃圾回收算法

    Java虚拟机的内存区域中,程序计数器.虚拟机栈和本地方法栈三个区域是线程私有的,随线程生而生,随线程灭而灭:栈中的栈帧随着方法的进入和退出而进行入栈和出栈操作,每个栈帧中分配多少内存基本上是在类结构 ...

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

    转自: http://www.jianshu.com/p/1e375fb40506 先来个概述,第二部分的画述才是厉害的. Garbage collection(GC) 现在的高级语言如java,c# ...

  8. C++ 几种经典的垃圾回收算法

    之前遇到了一篇好文(https://blog.csdn.net/wallwind/article/details/6889917)准备学习一下的,课程繁忙就忘记了,今日得闲,特来补一下. 自己写一遍加 ...

  9. JVM垃圾回收算法及回收器详解

    引言 本文主要讲述JVM中几种常见的垃圾回收算法和相关的垃圾回收器,以及常见的和GC相关的性能调优参数. GC Roots 我们先来了解一下在Java中是如何判断一个对象的生死的,有些语言比如Pyth ...

随机推荐

  1. ios面试题1

    iOS面试题   1.写一个NSString类的实现 + (id)initWithCString:(c*****t char *)nullTerminatedCString encoding:(NSS ...

  2. 好的Unix工具的九大启发

    我们每天都在使用前人开发的各种工具. 一款好的工具能无缝地融入到你的工作环境中,而一款"差"的工具经常须要花费额外的精力才干集成到你的工作环境中. (注意:这里的差是指用户体验方面 ...

  3. Hibernate是怎么工作的——Hibernate的工作流程

    举个简单的样例说明: 1.Base.java package cn.flymouse.hibernate; import java.util.Date; import org.hibernate.Qu ...

  4. mac鼠标滚动方向自然问题

    mac使用鼠标的时候滚轮方向和Windows是相反的.假设不勾选滚动方向自然,那么触摸板使用不爽. 解决的方法: 1.打开http://pilotmoon.com/scrollreverser/,下载 ...

  5. [NOIP2015模拟10.22] 最大子矩阵 解题报告(单调栈)

    Description 我们将矩阵A中位于第i行第j列的元素记作A[i,j].一个矩阵A是酷的仅当它满足下面的条件:       A[1,1]+A[r,s]<=A[1,s]+A[r,1](r,s ...

  6. 什么是MySQL?(一)

    课程大纲 第一章:初识MySQL 1.1什么是MySQL?  1.2如何安装MySQL?  1.3如何配置MySQL?  1.4如何启动和关闭MySQL?  1.5MySQL的登录和退出  1.6如何 ...

  7. Hadoop框架基础(五)

    ** Hadoop框架基础(五) 已经部署了Hadoop的完全分布式集群,我们知道NameNode节点的正常运行对于整个HDFS系统来说非常重要,如果NameNode宕掉了,那么整个HDFS就要整段垮 ...

  8. Java类和对象9

    (1)创建一个叫做机动车的类:属性:车牌号(String),车速(int),载重量(double)功能:加速(车速自增).减速(车速自减).修改车牌号,查询车的载重量.编写两个构造方法:一个没有形参, ...

  9. html中隐藏一个元素的方法

    display:none;                                                      隐藏不占位 opacity:0; fliter:alpha(opa ...

  10. PostgreSQL Replication之第四章 设置异步复制(2)

    4.2 配置级联复制 正如您在本章已经看到的,设置流复制真的很容易.只需要设置几个参数,做一个基础备份,并享受您的复制设置. 在许多情况下,这种情况更有一点点微妙.在这个例子中我们假设:我们要使用一个 ...