一、写在前面:

我们都知道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垃圾收集机制就会被触发,把那些可以被回收的对象回收掉,而那些不会回收的对象就会被移到中年代去,依此类推,老年代中的对象是存活时间最久的对象,甚至是存活于整个系统的生命周期内。

Python的垃圾回收机制(引用计数+标记清除+分代回收)的更多相关文章

  1. python9--内存管理 引用计数 标记清除 分代回收

     复习   文件处理 1.操作文件的三步骤 -- 打开文件:硬盘的空间被操作系统持有 | 文件对象被应用程序持续 -- 操作文件:读写操作 -- 释放文件:释放操作系统对硬盘空间的持有 2.基础的读写 ...

  2. Python垃圾回收详解:引用计数+标记清理+分代回收

    Python采用的是引用计数机制为主,标记-清理和分代收集两种机制为辅的策略. 1.引用计数 python中一切皆对象,所以python底层计数结构地就可以抽象为: 引用计数结构体{ 引用计数; 引用 ...

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

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

  4. day 21 垃圾回收机制、标记删除及分代回收

    垃圾回收机制 # 不能被程序访问到的数据,就称之为垃圾 引用计数 # 引用计数是用来记录值的内存地址被记录的次数的​# 每一次对值地址的引用都可以使该值的引用计数 +1# 每一次对值地址的释放都可以使 ...

  5. Android内存管理(11)*常见JVM回收机制「Java进程内存堆分代,JVM分代回收内存,三种垃圾回收器」

    参考: http://www.blogjava.net/rosen/archive/2010/05/21/321575.html 1,Java进程内存堆分代: 典型的JVM根据generation(代 ...

  6. JVM调优-Java垃圾回收之分代回收

    为什么要进行分代回收? JVM使用分代回收测试,是因为:不同的对象,生命周期是不一样的.因此不同生命周期的对象采用不同的收集方式. 可以提高垃圾回收的效率. Java程序运行过程中,会产生大量的对象, ...

  7. Python之美[从菜鸟到高手]--一步一步动手给Python写扩展(异常处理和引用计数)

    我们将继续一步一步动手给Python写扩展,通过上一篇我们学习了如何写扩展,本篇将介绍一些高级话题,如异常,引用计数问题等.强烈建议先看上一篇,Python之美[从菜鸟到高手]--一步一步动手给Pyt ...

  8. 浅谈Java堆内存分代回收

    目录 1.概述 2.堆内存是如何分代的 3.各分代之间是如何配合工作的 1.概述 与C++不同的是, 在Java中我们无需关心对象占用空间的释放, 这主要得益于Java中的垃圾处理器(简称GC)帮助我 ...

  9. jvm 分代回收算法通俗理解

    jvm区域总体分两类,heap区和非heap区.heap区又分:Eden Space(伊甸园).Survivor Space(幸存者区).Tenured Gen(老年代-养老区). 非heap区又分: ...

随机推荐

  1. hadoop环境运行程序出现 Retrying connect to server 问题

    程序运行时出现如下问题: 从网上查资料,有说重启format的..有说/etc/hosts出问题的... 反正都试了一遍..还是有这个问题 后来看日志,发现问题是访问服务器9001端口访问不到..开始 ...

  2. RabbitMQ In JAVA 介绍及使用

    介绍: RabbitMQ是开源的消息中间件,它是轻量级的,支持多种消息传递协议,可以部署在分布式和联合配置中,以满足高级别.高可用性需求.并且可在许多操作系统和云环境上运行,并为大多数流行语言提供了广 ...

  3. Flask入门之SQLAlchemy配置与数据库连接

    1. 安装SQLAlchemy pip install flask-sqlalchemy 2. 导入和配置 from flask_sqlalchemy import SQLAlchemy basedi ...

  4. swagger上传文件并支持jwt认证

    背景 由于swagger不仅提供了自动实现接口文档的说明而且支持页面调试,告别postman等工具,无需开发人员手动写api文档,缩减开发成本得到大家广泛认可 但是由于swagger没有提供上传文件的 ...

  5. Python并发编程之线程中的信息隔离(五)

    大家好,并发编程 进入第三篇. 上班第一天,大家应该比较忙吧.小明也是呢,所以今天的内容也很少.只要几分钟就能学完. 昨天我们说,线程与线程之间要通过消息通信来控制程序的执行. 讲完了消息通信,今天就 ...

  6. Java中判断是否为空的方法

    1.判断字符串或者对象是否为空 首先来看一下工具StringUtils的判断方法: 一种是org.apache.commons.lang3包下的: 另一种是org.springframework.ut ...

  7. Nginx虚拟主机

    [root@Nginx-server ~]# tar zxvf nginx-1.11.2.tar.gz [root@Nginx-server ~]# useradd -M -s /sbin/nolog ...

  8. SSM-SpringMVC-30:SpringMVC中InitBinder的骇客级优化

     ------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 上篇博客利用initbinder做了局部的日期类型转换,但是兼容性不要,只支持yyyy-MM-dd这种,所以 ...

  9. 小程序开发之图片转Base64(C#、.et)

    小程序页面代码因为某些人力不可控的代码丢失了,这里简单说明一下 调用小程序APIwx.chooseImage(OBJECT)选择相册或拍摄照片,会返回 tempFilePaths,之后通过wx.upl ...

  10. 7-20 jquery遍历节点,bootstrap模态框绑定事件和解绑,mock.js,model.urlroot,id,打基础

    7-19 1:$(event.target).parents().filter("tr").find("host-name") 为什么选择不到别的host-na ...