知行编程网知行编程网  2022-11-15 15:00 知行编程网 隐藏边栏  3 
文章评分 1 次,平均分 4.0
导语: 本文主要介绍了关于Python中的垃圾回收机制是什么的相关知识,希望可以帮到处于编程学习途中的小伙伴


Python中的垃圾回收机制是什么


一、写在前面:

我们都知道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)对象被创建:

Python中的垃圾回收机制是什么

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

(2)对象被引用:

Python中的垃圾回收机制是什么

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

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

Python中的垃圾回收机制是什么

这里很明显,引用计数在传递给函数后增加了 1。

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

Python中的垃圾回收机制是什么

这里我们在创建对象后分别添加一个列表和一个元组,并且引用计数递增。

虽然每次分配和释放内存时都必须加入引用计数来管理引用计数,但与其他垃圾回收技术相比,引用计数有一个优势,即“实时”,如果对象没有被引用,则内存会被直接释放,其他垃圾回收技术必须在一些特殊情况下才能回收无效内存。但是,引用计数带来的维护引用计数的额外操作与Python和引用中的内存分配和释放的分配次数成正比。此外,引用计数机制也有一个弱点——它不能解决循环引用带来的问题。循环引用可以使一个引用对象的引用计数不为0,但是这些对象实际上并没有被任何外部对象引用,它们只是相互引用,也就是说这组对象占用的内存空间应该被回收,但是循环引用引起的引用计数不为0,所以这组对象占用的内存空间永远不会被释放。如下,list1 和 list2 相互引用。如果没有其他对象引用它们,list1和list2的引用计数仍然为1,占用的内存永远无法回收,这将是致命的。

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

2、标记清除

Mark-Sweep 算法是一种基于 Tracing GC 技术的垃圾回收算法。分为两个阶段:第一阶段是标记阶段,GC会标记所有活动对象,第二阶段是回收那些没有标记的对象的非活动对象。

对象通过引用(指针)链接在一起形成有向图,对象形成这个有向图的节点,引用关系形成这个有向图的边。从根对象开始,沿有向边遍历对象。可达对象被标记为活动对象,不可达对象为待清除的非活动对象。根对象是全局变量、调用栈和寄存器。​​​

Python中的垃圾回收机制是什么

在上图中,块 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垃圾回收机制,回收那些可以回收的对象,而那些不能回收的则移到中年,以此类推,老年代的对象是存活时间最长的对象,即使在整个系统的生命周期中也是如此。

本文为原创文章,版权归所有,欢迎分享本文,转载请保留出处!

知行编程网
知行编程网 关注:1    粉丝:1
这个人很懒,什么都没写
扫一扫二维码分享