一、写在前面:

我们都知道Python一种面向对象的脚本语言,对象是Python中非常重要的一个概念。在Python中数字是对象,字符串是对象,任何事物都是对象,而它们的核心就是一个结构体--PyObject。

typedef struct_object{

  int ob_refcnt;

  struct_typeobject *ob_type;

}PyObject;

PyObject是每个对象必有的内容,其中ob_refcnt就是做为引用计数。

二、垃圾回收机制

垃圾回收(Garbage Collection)大家应该多多少少都听过,但是什么是垃圾回收呢?我们这里说的垃圾回收肯定不是把垃圾丢进垃圾桶。现在的高级语言Java,C#等,都采用了垃圾回收机制,而不再是C,C++里用户自己管理维护内存的方式,自己管理内存是很自由,但是可能出现内存泄漏,悬空指针等问题。而垃圾回收机制作为现代编程语言的自动内存管理机制,专注于两件事:1. 找到内存中无用的垃圾资源 2. 清除这些垃圾并把内存让出来给其他对象使用。

三、Python中的垃圾回收

在Python中,垃圾回收机制主要是以引用计数为主要手段,以标记清除和分代回收机制作为辅助手段实现的。

1、引用计数

通过前面的介绍,我们已经知道PyObject是每个对象必有的内容,而当一个对象有新的引用时,它的ob_refcnt就会增加,当引用它的对象被删除,它的ob_refcnt就会减少,当引用计数为0时,该对象生命就结束了。

我们来看看引用计数+1的情况有什么:

(1)对象被创建:

这里实际上123这个对象并没有在内存中新建,因为在Python启动解释器的时候会创建一个小整数池,在-5~256之间的整数对象会被自动加载到内存中等待调用。因此a=123是对123这个整数对象增加了一次引用。而456是不在整数池里的,需要创建对象,那么最后的引用次数是2呢?因为sys.getrefcount(b)也是一次引用。

(2)对象被引用:

每一次赋值操作都会增加数据的引用次数,要记住引用的变量a、b、c指向的是数据456,而不是变量本身。

(3)对象作为参数传递到函数中:

这里可以很明显看到在被传递到函数中后,引用计数增加了1。

(4)对象作为元素储存到容器中:

这里我们在创建对象之后,把a分别添加到了一个列表和一个元组中,引用计数都增加了。

虽然引用计数必须在每次分配合释放内存的时候加入管理引用计数的操作,然而与其他垃圾回收技术相比,引用计数有一个最大的优点,那就是“实时性”,如果这个对象没有引用,内存就直接释放了,而其他垃圾回收技术必须在某种特殊条件下才能进行无效内存的回收。但是引用计数带来的维护引用计数的额外操作和Python中进行的内存分配和释放,引用的赋值次数成正比的。除此之外,引用计数机制的还有一个最大的软肋--无法解决循环引用带来的问题。循环引用可以使一种引用对象的引用计数不为0,然而这些对象实际上并没有被任何外部对象所引用,它们之间只是相互引用,这意味着这组对象所占用的内存空间是应该被回收的,但是由于循环引用导致的引用计数不为0,所以这组对象所占用的内存空间永远不会被释放。如下,list1与list2相互引用,如果不存在其他对象对它们的引用,list1与list2的引用计数也仍然为1,所占用的内存永远无法被回收,这将是致命的。

list1 = []
list2 = []
list1.append(list2)
list2.append(list1)

2、标记清除

标记清除(Mark—Sweep)算法是一种基于追踪回收(tracing GC)技术实现的垃圾回收算法。它分为两个阶段:第一阶段是标记阶段,GC会把所有的活动对象打上标记,第二阶段是把那些没有标记的对象非活动对象进行回收。

对象之间通过引用(指针)连在一起,构成一个有向图,对象构成这个有向图的节点,而引用关系构成这个有向图的边。从根对象(root object)出发,沿着有向边遍历对象,可达的(reachable)对象标记为活动对象,不可达的对象就是要被清除的非活动对象。根对象就是全局变量、调用栈、寄存器。

           

在上图中,可以从程序变量直接访问块1,并且可以间接访问块2和3。程序无法访问块4和5。第一步将标记块1,并记住块2和3以供稍后处理。第二步将标记块2,第三步将标记块3,但不记得块2,因为它已被标记。扫描阶段将忽略块1,2和3,因为它们已被标记,但会回收块4和5。

标记清除算法作为Python的辅助垃圾收集技术,主要处理的是一些容器对象,比如list、dict、tuple等,因为对于字符串、数值对象是不可能造成循环引用问题。Python使用一个双向链表将这些容器对象组织起来。不过,这种简单粗暴的标记清除算法也有明显的缺点:清除非活动的对象前它必须顺序扫描整个堆内存,哪怕只剩下小部分活动对象也要扫描所有对象。

3、分代回收

分代回收是建立在标记清除技术基础之上的,是一种以空间换时间的操作方式。

Python将内存根据对象的存活时间划分为不同的集合,每个集合称为一个代,Python将内存分为了3“代”,分别为年轻代(第0代)、中年代(第1代)、老年代(第2代),他们对应的是3个链表,它们的垃圾收集频率与对象的存活时间的增大而减小。新创建的对象都会分配在年轻代,年轻代链表的总数达到上限时,Python垃圾收集机制就会被触发,把那些可以被回收的对象回收掉,而那些不会回收的对象就会被移到中年代去,依此类推,老年代中的对象是存活时间最久的对象,甚至是存活于整个系统的生命周期内。

转载于:https://www.cnblogs.com/TM0831/p/10599716.html

Python的垃圾回收机制(引用计数+标记清除+分代回收)相关推荐

  1. PHP垃圾回收机制----引用计数基础知识

    <?php // PHP垃圾回收机制案例: 参考php手册// --------------------标量类型-------------------- // tip:每个php变量存在一个叫& ...

  2. 《对Java的分析总结》-Java中的垃圾回收机制中的标记-清除算法 (五)

    标记-清除算法 标记-清除算法(mark-sweep 1 标记-清除算法 回收过程描述 类别 描述 mutator 设置 collector 收集 mutator roots mutator根对象 1 ...

  3. 垃圾回收:引用计数与分代收集机制

    当引⽤计数为0时,该对象⽣命就结束了. 引⽤计数机制的优点: 1.简单 2.实时性:⼀旦没有引⽤,内存就直接释放了.不⽤像其他机制等到特定时机.实时性还带来⼀个好处:处理回收内存的时间分摊到了平时. ...

  4. 【Android 内存优化】Java 内存模型 ( Java 虚拟机内存模型 | 线程私有区 | 共享数据区 | 内存回收算法 | 引用计数 | 可达性分析 )

    文章目录 一. Java 虚拟机内存模型 二. 程序计数器 ( 线程私有区 ) 三. 虚拟机栈 ( 线程私有区 ) 四. 本地方法栈 ( 线程私有区 ) 五. 方法区 ( 共享数据区 ) 1. 方法区 ...

  5. JVM分代回收机制和垃圾回收算法

    JVM系列文章目录 初识JVM 深入理解JVM内存区域 玩转JVM对象和引用 JVM分代回收机制和垃圾回收算法 细谈JVM垃圾回收与部分底层实现 Class文件结构及深入字节码指令 玩转类加载和类加载 ...

  6. python垃圾回收机制为什么标记能解决循环引用问题_python 关于循环引用以及标记清除的问题...

    1 在循环引用的情况下,引用计数就不好事了,这时候就需要用到标记清除 循环引用的危害: 会造成内存溢出,因为循环引用计数不可能为零 解决方法: 标记清除 2 关于标记清除的效率问题(低) 引用计数引用 ...

  7. 【Java 虚拟机原理】垃圾回收算法 ( Java 虚拟机内存分区 | 垃圾回收机制 | 引用计数器算法 | 引用计数循环引用弊端 )

    文章目录 一.Java 虚拟机内存分区 二.垃圾回收机制 三.引用计数器算法 ( 无法解决循环引用问题 ) 一.Java 虚拟机内存分区 Java 虚拟机内存分区 : 所有线程共有的内存区域 : 堆 ...

  8. python分代回收_python 垃圾回收——分代回收 和java有些区别 注意循环引用无法被回收...

    作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 语言的内存管理是语言设计的一个重要方面.它是决定语言性能的重要因素.无论是C语言的 ...

  9. jvm垃圾回收机制_JVM 垃圾回收机制之堆的分代回收

    JVM垃圾回收机制之堆的分代回收 前言 前文我们了解了Java的GC机制,对于堆中的对象,JVM采用引用计数和可达性分析两种算法来标记对象是否可以清除,本文中我们还会了解到JVM将对分成了不同的区域, ...

最新文章

  1. 正确的 Git 提交记录和分支模型
  2. Spring Boot怎么样注册Servlet三大组件[Servlet、Filter、Listener]
  3. Nacos(八)之Docker
  4. mysql5.7安装差异_mysql5.7和mysql5.6同在CentOS7.4安装差异对比之5.7.18
  5. QT保留小数点后几位
  6. python四大器_Python编程四大神兽:迭代器、生成器、闭包和装饰器
  7. swift int转string_用Swift开发macOS程序,九、目录模块
  8. 多线程中的静态代理模式
  9. ztree 标准得json数据格式_Django+zTree构建组织架构树
  10. TCP/UDP压力测试工具
  11. php 实现rpc,使用php链接jsonrpc服务
  12. ffmpeg处理YUV422和YUV420P相互转换
  13. SVCHOST启动服务实战
  14. 快门(曝光时间)、光圈、感光度(ISO)的关系
  15. 【7】OPencv骨架细化算法
  16. gdrive无限网盘挂载systemd文件
  17. jop怎么读音英语怎么说_job的意思在线翻译,解释job中文英文含义,短语词组,音标读音,例句,词源,同义词【澳典网ODict.Net】...
  18. 如何去开发一个webApp
  19. 计算机辅助制造工程师什么,cam工程师是什么意思
  20. VS2013编译libjpeg库

热门文章

  1. 940m显卡 出现跳动的小点_启用4K 120Hz后NVIDIA安培显卡黑屏 原因找到:HDMI 2.1芯片BUG导致...
  2. php 图片处理类,分享php多功能图片处理类
  3. nullable java_java-持久性@Column nullable = false可以插入null
  4. php项目后的感受,PHP项目感受4 - 刘景刘恒的个人空间 - OSCHINA - 中文开源技术交流社区...
  5. js html css淘宝足迹日历效果,js实现简单的日历显示效果函数示例
  6. Pycharm中tensorflow框架下tqdm的安装
  7. 压缩感知专题笔记——目录
  8. Loadrunner脚本开发简单几种方式
  9. 基于MSM 的tomcat session 共享
  10. 磁盘空间神秘消失事件