热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

pythongc模块_python|gc模块有哪几种

2、Python中的GCPython中的GC,以引用计数为主,标记-清除和分代回收为辅。2.1、引用计数(referencecounting)引用计数&

2、Python中的GC

Python中的GC,以引用计数为主,标记-清除和分代回收为辅。

2.1、引用计数(reference counting)

引用计数,是George E. Collins在1960年发明的,算是最早期的垃圾回收实现方法。

在Python中,每一个对象的核心就是一个结构体PyObject,它的内部有一个引用计数器ob_refcnt:

typedef struct_object {

int ob_refcnt;

struct_typeobject*ob_type;

} PyObject;

当一个对象有新的引用时,对象的引用计数+1;当一个对象的引用被销毁时,对象的引用计数-1;当对象的引用计数减少为0时,就意味着对象已经没有被任何人使用了,可以将其所占用的内存释放。

导致引用计数+1的情况:1).对象被创建;2).对象被复制;3).对象作为函数参数被传入(引用计数+2);4).对象作为一个元素被存储在容器中.

导致引用计数-1的情况:1).对象的别名被显式销毁;2).对象的别名被赋予新的对象;3).一个对象离开它的作用域,例如函数执行完毕后,其中的局部变量;4).对象所在的容器被销毁,或从容器中删除对象。

引用计数机制的优缺点是显而易见的:

优点:

1. 简单;

2. 实时性:一旦引用计数为0,立即被回收;内存回收的时间分摊到平时;

缺点:

1. 需要额外的空间来维护引用计数;

2. 执行效率低:引用计数机制所带来的维护引用计数的额外操作,与程序运行过程中所进行的内存分配、释放和引用赋值的次数成正比

除了上面提到的,引用计数机制还有一个致命缺点,即无法解决循环引用的问题。我们用一段代码来做进一步的解释:

a = [1, 2] #对象[1, 2]的引用计数为1

b = [3, 4] #对象[3, 4]的引用计数为1

a.append(b) #对象[3, 4]的引用计数为2

b.append(a) #对象[1, 2]的引用计数为2

del a #对象[1, 2]的引用计数为1

del b #对象[3, 4]的引用计数为1

上面的代码中,对象[1, 2]和[3, 4]已经没有了来自外界的引用,这意味着不会再有人使用它们(无法通过其它变量来引用这两个对象),但是它们彼此之间依然有相互的引用,因此引用计数均为1,也就导致它们的内存永远不能被回收。

这一点是致命的,它与手动进行内存管理所产生的内存泄漏无异(因此,也有很多语言比如Java并没有采用引用计数来实现GC)。为了弥补引用计数的缺陷,Python中引入了其它的GC机制。

2.2、标记-清除(mark and sweep)

可以包含其它对象引用的容器对象,如list、set、dict、class、instance,都可能产生循环引用,标记-清除可以解决这个问题。

标记-清除是一种基于追踪(Tracing)回收技术实现的垃圾回收算法。它分为两个阶段:第一阶段是标记,把所有的『活动对象』打上标记,第二阶段是回收,对那些没有标记的『非活动对象』进行回收。那么,如何区分活动对象和非活动对象呢?

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

在上图中,我们把小黑圈视为root object,从小黑圈出发,对象1可达,那么它将被标记,对象2、3可间接可达也会被标记,而4和5不可达,那么1、2、3就是活动对象,4和5是非活动对象会被GC回收。

标记-清除的过程实际比上面说的还要复杂一下,具体来讲,首先找到root object集合,然后在内存中建立两条链表,一条链表中维护root object集合,称为root链表,而另外一条链表中维护剩下的对象,称为unreachable链表。在标记的过程中,如果发现unreachable链表中存在被root链表中的对象,直接或间接引用的对象,就将其从unreachable链表中移到root链表中;当完成标记后,unreachable链表中剩下的所有对象就是名副其实的垃圾对象了,接下来的垃圾回收只需限制在unreachable链表中即可。

2.3、分代回收(generation collection)

分代回收,是一种以空间换时间的回收方式,可以提升GC的效率。

分代回收思想将对象分在不同的集合中,每个集合称为一个“代”(generation),Python中分为3代,分别为年轻代(第0代)、中年代(第1代)、老年代(第2代),他们对应的是3个链表。每一代的GC频率是不同的,第0代最高,第1代次之,第2代最低。

根据弱代假说(即越年轻的对象越容易死掉,而老的对象通常会存活更久),新生的对象被放入第0代,如果该对象在第0代的一次GC中活了下来,那么它就被移动到第1代,类似地,如果某第1代对象在第1代的一次GC中活了下来,它就被移动到第2代。

那么,什么情况下会触发GC呢?具体地,在Python中,gc.set_threshold(threshold0[,threshold1[,threshold2]])可以设置每一代GC被触发的阈值:从上一次第0代GC后,如果分配对象的个数减去释放对象的个数大于threshold0,那么就会对第0代中的对象进行GC; 从上一次第1代GC后,如果第0代被GC的次数大于threshold1,那么就会对第1代中的对象进行GC;同样,从上一次第2代GC后,如果第1代被GC的次数大于threshold2,那么就会对第2代中的对象进行GC。除此之外,还有两种情况会触发GC,第一种是手动调用gc.collect(),第二种便是程序退出。

从上面的叙述可以看出,老年代中的对象是存活时间最久的对象,甚至是存活于整个系统的生命周期内。

2.4、其它

a).

Python中的gc模块提供了一些接口给开发者设置GC相关的选项,具体使用可参考1和2。

b).

如果循环引用中,两个对象都定义了__del__方法,gc模块不会销毁这两个不可达对象,因为gc模块不知道应该先调用哪个对象的__del__方法(例如,两个对象a和b,如果先销毁a,则在销毁b时,会调用b的__del__方法,该方法中很可能使用了a,这时会造成异常),所以为了安全起见,gc模块会把对象放到gc.garbage中,并把它们称为uncollectable。很明显,这种情况会造成内存泄漏,要解决的话,只能显式调用其中某个对象的__del__方法来打破僵局。

c).

还有一种情况会造成Python中的内存泄漏,即对象一直被全局变量所引用,而我们知道,全局变量的生命周期是非常长的。

2.5、小结

写到这里,我们尝试着来小结一下Python中的GC机制:Python中,对于所有对象,引用计数都在起作用,一旦某对象的引用计数为0,它所占用的内存就会被释放;而对于容器对象,由于它们会产生循环引用,这是引用计数所无法解决的,于是Python引入了标记-清除的方式来对它们做GC;最后,为了提升标记-清除的GC效率,Python引入了分代回收的机制,以空间换时间。



推荐阅读
  • 本文介绍了H5游戏性能优化和调试技巧,包括从问题表象出发进行优化、排除外部问题导致的卡顿、帧率设定、减少drawcall的方法、UI优化和图集渲染等八个理念。对于游戏程序员来说,解决游戏性能问题是一个关键的任务,本文提供了一些有用的参考价值。摘要长度为183字。 ... [详细]
  • 本文介绍了Java的集合及其实现类,包括数据结构、抽象类和具体实现类的关系,详细介绍了List接口及其实现类ArrayList的基本操作和特点。文章通过提供相关参考文档和链接,帮助读者更好地理解和使用Java的集合类。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 标题: ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 集合的遍历方式及其局限性
    本文介绍了Java中集合的遍历方式,重点介绍了for-each语句的用法和优势。同时指出了for-each语句无法引用数组或集合的索引的局限性。通过示例代码展示了for-each语句的使用方法,并提供了改写为for语句版本的方法。 ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • LeetCode笔记:剑指Offer 41. 数据流中的中位数(Java、堆、优先队列、知识点)
    本文介绍了LeetCode剑指Offer 41题的解题思路和代码实现,主要涉及了Java中的优先队列和堆排序的知识点。优先队列是Queue接口的实现,可以对其中的元素进行排序,采用小顶堆的方式进行排序。本文还介绍了Java中queue的offer、poll、add、remove、element、peek等方法的区别和用法。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
  • 设计模式——模板方法模式的应用和优缺点
    本文介绍了设计模式中的模板方法模式,包括其定义、应用、优点、缺点和使用场景。模板方法模式是一种基于继承的代码复用技术,通过将复杂流程的实现步骤封装在基本方法中,并在抽象父类中定义模板方法的执行次序,子类可以覆盖某些步骤,实现相同的算法框架的不同功能。该模式在软件开发中具有广泛的应用价值。 ... [详细]
author-avatar
Eva绫波_772
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有