http://blog.csdn.net/pipisorry/article/details/39647931

Python GC主要使用引用计数(reference counting)来跟踪和回收垃圾。在引用计数的基础上,通过“标记-清除”(mark and sweep)解决容器对象可能产生的循环引用问题,通过“分代回收”(generation collection)以空间换时间的方法提高垃圾回收效率。也就是Python中的垃圾回收是以引用计数为主,分代收集为辅。

引用计数

概述

引用计数法在对象内部维护了一个被其它对象引用数的引用计数值。当这个引用计数值为0时。说明这个对象不再被其它对象引用,就能够被回收了。

结合源代码来看,全部Python对象的头部包括了这样一个结构PyObject(相当于继承自PyObject):

// object.h
struct _object {
Py_ssize_t ob_refcnt;
struct PyTypeObject *ob_type;
} PyObject;

ob_refcnt就是引用计数值。

比如,以下是int型对象的定义:

// intobject.h
typedef struct {
PyObject_HEAD
long ob_ival;
} PyIntObject;

引用计数的增减

导致引用计数+1的情况
        对象被创建,比如a=23
        对象被引用,比如b=a
        对象被作为參数,传入到一个函数中,比如func(a)
        对象作为一个元素。存储在容器中,比如list1=[a,a]
    导致引用计数-1的情况
        对象的别名被显式销毁。比如del a
        对象的别名被赋予新的对象,比如a=24
        一个对象离开它的作用域,比如f函数运行完成时,func函数中的局部变量(全局变量不会)
        对象所在的容器被销毁。或从容器中删除对象

引用计数演示样例

def func(c):
    print 'in func function', sys.getrefcount(c) - 1

print 'init', sys.getrefcount(11) - 1
a = 11
print 'after a=11', sys.getrefcount(11) - 1
b = a
print 'after b=1', sys.getrefcount(11) - 1
func(11)
print 'after func(a)', sys.getrefcount(11) - 1
list1 = [a, 12, 14]
print 'after list1=[a,12,14]', sys.getrefcount(11) - 1
a=12
print 'after a=12', sys.getrefcount(11) - 1
del a
print 'after del a', sys.getrefcount(11) - 1
del b
print 'after del b', sys.getrefcount(11) - 1
# list1.pop(0)
# print 'after pop list1',sys.getrefcount(11)-1
del list1
print 'after del list1', sys.getrefcount(11) - 1

init 24
after a=11 25
after b=1 26
in func function 28
after func(a) 26
after list1=[a,12,14] 27
after a=12 26
after del a 26
after del b 25
after del list1 24

查看一个对象的引用计数:sys.getrefcount(a)能够查看a对象的引用计数。可是比正常计数大1,因为调用函数getrefcount的时候传入a。这也会让a的引用计数+1。

循环引用导致内存泄露

def f2():
    while True:
        c1=ClassA()
        c2=ClassA()
        c1.t=c2
        c2.t=c1
        del c1
        del c2

运行f2()。进程占用的内存会不断增大。

创建了c1,c2后,0x237cf30(c1相应的内存,记为内存1),0x237cf58(c2相应的内存,记为内存2)这两块内存的引用计数都是1,运行c1.t=c2c2.t=c1后,这两块内存的引用计数变成2。在del c1后。内存1的对象的引用计数变为1。因为不是为0,所以内存1的对象不会被销毁,所以内存2的对象的引用数依旧是2,在del c2后,同理,内存1的对象,内存2的对象的引用数都是1。
尽管它们两个的对象都是能够被销毁的。可是因为循环引用,导致垃圾回收器都不会回收它们,所以就会导致内存泄露。

引用计数法优缺点

引用计数法有非常明显的长处
    高效
    运行期没有停顿
    对象有确定的生命周期
    易于实现
原始的引用计数法也有明显的缺点:
    维护引用计数的次数和引用赋值成正比,而不像mark and sweep等基本与回收的内存数量有关。

无法解决循环引用的问题。A和B相互引用而再没有外部引用A与B中的不论什么一个,它们的引用计数都为1,但显然应该被回收。
为了解决这两个致命弱点,Python又引入了以下两种GC机制。

皮皮blog

标记-清除

“标记-清除”法是为了解决循环引用问题。能够包括其它对象引用的容器对象(如list, dict, set,甚至class)都可能产生循环引用,为此,在申请内存时,全部容器对象的头部又加上了PyGC_Head来实现“标记-清除”机制。

// objimpl.h
typedef union _gc_head {
struct {
union _gc_head *gc_next;
union _gc_head *gc_prev;
Py_ssize_t gc_refs;
} gc;
long double dummy; /* force worst-case alignment */
} PyGC_Head;

垃圾标记时,先将集合中对象的引用计数复制一份副本(以免在操作过程中破坏真实的引用计数值)。然后操作这个副本,遍历对象集合,将被引用对象的引用计数副本值减1。

然后依据引用计数副本值是否为0将集合内的对象分成两类。reachable和unreachable,当中unreachable是能够被回收的对象。在处理了weak reference和finalizer等琐碎细节后(本文不展开讲述,有兴趣的请參考python源代码),就能够回收unreachable中的对象了。

皮皮blog

分代回收

分代回收的总体思想是:将系统中的全部内存块依据其存活时间划分为不同的集合。每一个集合就成为一个“代”,垃圾收集频率随着“代”的存活时间的增大而减小,存活时间通常利用经过几次垃圾回收来度量。

用来表示“代”的结构体是gc_generation。 包括了当前代链表表头、对象数量上限、当前对象数量:

// gcmodule.c
struct gc_generation {
PyGC_Head head;
int threshold; /* collection threshold */
int count; /* count of allocations or collections of younger
generations */
};

Python默认定义了三代对象集合。索引数越大。对象存活时间越长。

新生成的对象会被增加第0代,前面_PyObject_GC_Malloc中省略的部分就是Python GC触发的时机。每新生成一个对象都会检查第0代有没有满。假设满了就開始着手进行垃圾回收。

皮皮blog

gc模块经常使用功能解析

Garbage Collector interface
gc模块提供一个接口给开发人员设置垃圾回收的选项。

上面说到,採用引用计数的方法管理内存的一个缺陷是循环引用。而gc模块的一个主要功能就是解决循环引用的问题。

应用

项目中避免循环引用
    引入gc模块。启动gc模块的自己主动清理循环引用的对象机制
    因为分代收集,所以把须要长期使用的变量集中管理。并尽快移到二代以后,降低GC检查时的消耗
    gc模块唯一处理不了的是循环引用的类都有__del__方法,所以项目中要避免定义__del__方法,假设一定要使用该方法,同一时候导致了循环引用。须要代码显式调用gc.garbage里面的对象的__del__来打破僵局

from:http://blog.csdn.net/pipisorry/article/details/39647931

ref: [python的内存管理机制]

[《Python源代码剖析》,陈儒著,2008]

[Python垃圾回收机制具体解释]

[Python垃圾回收机制]*

Python内存管理:垃圾回收的更多相关文章

  1. python内存管理&垃圾回收

    python内存管理&垃圾回收 引用计数器 环装双向列表refchain 在python程序中创建的任何对象都会放在refchain连表中 name = '张三' age = 18 hobby ...

  2. [Python之路] 内存管理&垃圾回收

    一.python源码 1.准备源码 下载Python源码:https://www.python.org/ftp/python/3.8.0/Python-3.8.0.tgz 解压得到文件夹: 我们主要关 ...

  3. Java 类加载机制 ClassLoader Class.forName 内存管理 垃圾回收GC

    [转载] :http://my.oschina.net/rouchongzi/blog/171046 Java之类加载机制 类加载是Java程序运行的第一步,研究类的加载有助于了解JVM执行过程,并指 ...

  4. 内存管理 垃圾回收 C语言内存分配 垃圾回收3大算法 引用计数3个缺点

    小结: 1.垃圾回收的本质:找到并回收不再被使用的内存空间: 2.标记清除方式和复制收集方式的对比: 3.复制收集方式的局部性优点: https://en.wikipedia.org/wiki/C_( ...

  5. [CLR via C#]21. 自动内存管理(垃圾回收机制)

    目录 理解垃圾回收平台的基本工作原理 垃圾回收算法 垃圾回收与调试 使用终结操作来释放本地资源 对托管资源使用终结操作 是什么导致Finalize方法被调用 终结操作揭秘 Dispose模式:强制对象 ...

  6. 【python测试开发栈】—python内存管理机制(二)—垃圾回收

    在上一篇文章中(python 内存管理机制-引用计数)中,我们介绍了python内存管理机制中的引用计数,python正是通过它来有效的管理内存.今天来介绍python的垃圾回收,其主要策略是引用计数 ...

  7. python内存管理及垃圾回收

    一.python的内存管理 python内部将所有类型分成两种,一种由单个元素组成,一种由多个元素组成.利用不同结构体进行区分 /* Nothing is actually declared to b ...

  8. Python 内存管理与垃圾回收

    Python 内存管理与垃圾回收 参考文献:https://pythonav.com/wiki/detail/6/88/ 引用计数器为主标记清除和分代回收为辅 + 缓存机制 1.1 大管家refcha ...

  9. 转发:[Python]内存管理

    本文为转发,原地址为:http://chenrudan.github.io/blog/2016/04/23/pythonmemorycontrol.html 本文主要为了解释清楚python的内存管理 ...

随机推荐

  1. AGC 025 B - RGB Coloring

    B - RGB Coloring Time limit : 2sec / Memory limit : 1024MB Score : 700 points Problem Statement Taka ...

  2. Codechef REBXOR

    Read problems statements in Mandarin and Russian. Translations in Vietnamese to be uploaded soon. Ni ...

  3. 【单调队列】bzoj1047 [HAOI2007]理想的正方形

    先把整个矩阵处理成b[n][m-K+1].c[n][m-K+1]大小的两个矩阵,分别存储每行每K个数中的最大.最小值,然后再通过b.c处理出d.e分别表示K*K大小的子矩阵中的最大.最小值即可.单调队 ...

  4. 倒置输入的数 Exercise07_02

    import java.util.Scanner; /** * @author 冰樱梦 * 时间:2018年下半年 * 题目:倒置输入的数 * */ public class Exercise07_0 ...

  5. Problem S: 零起点学算法14——三位数反转

    #include<stdio.h> #include<stdlib.h> int main() { int a,b,c,s; scanf("%d",& ...

  6. @RequestMapping注解的使用,Controller方法返回值

    1,web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app version=" ...

  7. NSPredicate 谓词总结 数组过滤 模糊匹配

    NSPredicate 用于指定过滤条件,主要用于从集合中分拣出符合条件的对象,也可以用于字符串的正则匹配. NSPredicate常用方法介绍 1.创建NSPredicate(相当于创建一个过滤条件 ...

  8. Why DNS Based Global Server Load Balancing (GSLB) Doesn’t Work

    Why DNS Based Global Server Load Balancing (GSLB) Doesn't Work    

  9. nativeexcel将excel导入数据集

    nativeexcel将excel导入数据集 uses nexcel; procedure Tfgoods.daoruExecute(Sender: TObject);var od: TOpenDia ...

  10. java.sql.SQLException: Io 异常: Got minus one from a read call

    博客分类: Oracle   Tomcat服务器下的应用连接Oracle时报错,出现以下异常: java.sql.SQLException: Io 异常: Got minus one from a r ...