概览:

      主要通过 引用计数来进行垃圾收集, 就是说,当一个对象没有被其他对象引用的时候,会释放掉内存。
    但是会有一些循环引用的对象,通过上面的方法,是没有办法清除掉的。所以,python还有另外的一个机制来解决这个问题,那就是标记-清除。

标记-清除:

        主要过程为, 扫描所有容器对象(不会扫描int, string,这些简单对象,因为他们不能包含其他对象的引用,不会造成循环引用),通过一种方法将这些对象分为两部分,一部分表示可以被删除,一部分表示不可被删除,然后将可以被删除的对象回收掉。
    那么首先一个问题,如何组织这些container对象呢?python采用了双向链表来跟踪这些对象,所有的container对象在创建之后,都会被插入到链表里。每个container对象里面都会有一个 PyGC_HEAD结构。
  1. typedef union _gc_head {
  2. struct {
  3. union _gc_head *gc_next;
  4. union _gc_head *gc_prev;
  5. Py_ssize_t gc_refs;
  6. } gc;
  7. double dummy; /* force worst-case alignment */
  8. } PyGC_Head;
这个结构中,很明显,gc_next 和 gc_prev 是实现双向链表的两个指针,  gc_refs在后面解决双向引用的时候会用到。
    通过上面的方法,每当有container对象被创建,就会将他加入到内部维护的可收集对象链表里。这样每次执行垃圾收集的时候,就可以遍历这些链表,进行标记清除啦。但是这样又存在一个问题,在执行垃圾收集的时候,程序是被暂停执行的,垃圾收集结束之后才会继续执行。 每次执行垃圾收集,都需要遍历所有的container对象,如果当前进程中对象比较多的话,会影响程序的执行效率。 一个优化的方法就是:分代收集

分代收集

       分代收集基于这样一个统计事实: 程序执行期间,有一部分内存块会在很短的时间分配,然后又被释放。而那些存活时间越久的内存块就越不容易被释放,甚至可能在程序执行期间一直存活,事实上这部分所占的比例还不小。所以,python将系统中存活的内存块根据其存活时间将其分为三个不同的代(年轻代, 青年代, 老年代)。每一个代对应上面描述的一个双向链表。新创建的container对象会被加入到年轻代中,如果他经历了几次回收之后依然存活,那么就把他放入青年代。依次类推。这样可以降低老年代和青年代的扫描频率。因为越老的内存块,扫描之后可以被释放的几率越小, 所以优先扫描年轻的代。
    每一个代(双向链表)所容纳的对象个数是有限的,当超过了这个限制,就会触发一次 标记-清除 的过程。目前的版本(python3.5.2, 年轻代是700, 青年代和老年代是10)
    那么,如何进行标记-清除的过程呢。比如我们要对青年代进行一次垃圾收集。我们需要从这个链表中的对象中 找到那些被可收集链表以外的对象引用的对象放入 root object集合。然后从这些root object开始遍历可收集链表,从中找出需要删除的对象(也就是存在循环引用的对象)放入unreachable集合里面。然后对unreachable集合中的对象执行回收。 如何找到哪些对象是循环引用的呢?假设对象A,和 B 的引用计数都为1,但是A引用了B,B同时引用了A,所以A和B是可以被回收的。我们需要识别出这种对象,把他放入unreachable集合里。所以我们首先需要解除摘除循环引用,我们首先遍历可收集链表,将其中每个对象的引用计数都减1,这样A和B的引用计数都变为了0。这样,剩下的对象中,如果他的引用计数仍然大于0,说明这个对象不可被删除(因为不止有一个对象在引用他),我们把它放入root object集合里。(ps:这里有个问题,假入对对象C的引用计数减1,这时候对象C的引用计数为0,但是其实C是有其他对象在引用的,那岂不是出问题了,这里就用到了上面PyGC_Head 里面的gc_refs, 也就是并不是直接操作对象C的引用计数,而是拷贝了一个副本,这个变量就是用来干这个的)。
    现在我们有了一个root object集合,这个集合里的对象是不能被删除的。所以,这些对象引用的对象也是不可被删除的。现在只需要遍历可收集链表,就可以找到unreacheble对象。完成标记之后,执行回收。
 
注意:

当python中自己实现了 __del__() 方法时,对于这样的对象,跟据 Python 的定义,在释放该对象占用的资源前需要调用该函数。由于 Python 的垃圾回收机制不能保证垃圾回收的顺序,可能在删除 b 之后,在 a.__del__() 中仍然会调用 b 对象,这样将会造成异常。

为此,Python 采取了比较保守的策略,也就是说对于当自定的类,如果存在 __del__() 时,不会对该对象进行垃圾回收。这样的对象,Python 会直接放到一个 garbage 列表中,这个列表在运行期间不会释放,对于 Python 来说不会有内存泄露,但是对于该程序来说,实际上已经发生了内存泄露

python垃圾回收机制的一些理解的更多相关文章

  1. 简述Python垃圾回收机制和常量池的验证

    目录 通过代码验证python解释器内部使用了常量池 Python的引入 变量的引入 为什么要有变量 定义变量 常量引入 常量池引入 Python解释器 Python变量存储机制 Python垃圾回收 ...

  2. python垃圾回收机制与小整数池

    python垃圾回收机制 当引用计数为0时,python会删除这个值. 引用计数 x = 10 y = x del x print(y) 10 引用计数+1,引用计数+1,引用计数-1,此时引用计数为 ...

  3. python垃圾回收机制:引用计数 VS js垃圾回收机制:标记清除

    js垃圾回收机制:标记清除 Js具有自动垃圾回收机制.垃圾收集器会按照固定的时间间隔周期性的执行. JS中最常见的垃圾回收方式是标记清除. 工作原理 当变量进入环境时,将这个变量标记为"进入 ...

  4. 浅析Python垃圾回收机制!

    Python垃圾回收机制 目录 Python垃圾回收机制 1. 内存泄露 2. Python什么时候启动垃圾回收机制? 2.1 计数引用 2.2 循环引用 问题:引用计数是0是启动垃圾回收的充要条件吗 ...

  5. 从 CPython 源码角度看 Python 垃圾回收机制

    环状双向链表 refchain 在 Python 程序中创建的任何对象都会被放到 refchain 链表中,当创建一个 Python 对象时,内部实际上创建了一些基本的数据: 上一个对象 下一个对象 ...

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

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

  7. python垃圾回收机制(Garbage collection)

    由于面试中遇到了垃圾回收的问题,转载学习和总结这个问题. 在C/C++中采用用户自己管理维护内存的方式.自己管理内存极其自由,可以任意申请内存,但也为大量内存泄露.悬空指针等bug埋下隐患. 因此在现 ...

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

    构造函数与析构函数 构造函数: 用于初始化类的内容部状态,Python提供的构造函数式 __init__(); 也就是当该类被实例化的时候就会执行该函数.那么我们就可以把要先初始化的属性放到这个函数里 ...

  9. 浅谈python垃圾回收机制

    引入 ​ 解释器在执行到定义变量的语法时,会申请内存空间来存放变量的值,而内存的容量是有限的,这就涉及到变量值所占用内存空间的回收问题,当一个变量值没有用了(简称垃圾)就应该将其占用的内存给回收掉,那 ...

随机推荐

  1. Windows phone 8.1布局控件

    布局控件(4种  第一种) Grid:相当于 HTML 中的 Table 标签,但是注意 Table 更重要的是展示数据,   而 Grid 则是专门为布局所生 属性标记: Grid.RowDefin ...

  2. kettle系列-2.kettle源码结构分析

    kettle是一个开源产品,产品本身设计是很优秀的,代码应该是很多开源爱好者用业余时间贡献的,代码整体结构还是比较容易理解的,但具体到每一个控件内部就因人而异了,感觉还是挺复杂的,肯定别人考虑得比较全 ...

  3. python莫名其妙的yield, yield from, yield.send

    练了几行代码, 慢慢找感觉. TASK,多线程,异步,很多地方都用到的呢. #!/usr/bin/env python # -*- coding: utf-8 -*- import time from ...

  4. [译]SSAS下玩转PowerShell(二)

    上一篇中简单的介绍了SSAS下的PowerShell,这一篇会演示更多的操作,比如根据当前时间创建备份,使用变量去指定处理哪一个分区,以及用XMLA脚本去创建分区,和在PowerShell中调用Pow ...

  5. storm集群部署和配置过程详解

      先整体介绍一下搭建storm集群的步骤: 设置zookeeper集群 安装依赖到所有nimbus和worker节点 下载并解压storm发布版本到所有nimbus和worker节点 配置storm ...

  6. jquery placeholder

    /* * @author ambar * html5 placeholder pollfill * - 使用绝对定位内嵌层 * - 也适用于密码域 * 目标浏览器: IE 6~9, FF 3.5 */ ...

  7. cas单点登录时报Invalid credentials

    CAS4后默认的密码验证不是简单的相同了.在配置文件deployerConfigContext.xml里可以找到, 默认是 Username:casuser Password:Mellon

  8. Node.js-视图引擎【1】-Swig集成express的安装与配置

    node.js视图引擎,选来选去发现Swig最符合我的胃口哈哈. 一.安装Swig视图引擎 npm install -g swig 二.在node.js代码中配置如下 var app = requir ...

  9. MySQL 存储过程控制语句

    变量作用域内部的变量在其作用域范围内享有更高的优先权,当执行到end.变量时,内部变量消失,此时已经在其作用域外,变量不再可见了,应为在存储过程外再也不能找到这个申明的变量,但是你可以通过out参数或 ...

  10. 【BO】安装BO服务器时,oracle服务端安装ora-12514和12541的问题

    今天在安装BO服务器,oracle数据库时,出现了这样一个问题,描述如下: 首先安装oracle10g Server 32位版.安装ORCL数据库之后,使用10gServer下的NET MANAGER ...