Python-垃圾回收

Python 中内存的申请和回收,不再像 C、C++ 那样需要程序员自己手动分配和回收内存,而是拥有自己的自动垃圾回收机制,程序员无需考虑内存分配回收问题。

Python 垃圾回收的策略:采用引用计数机制为主,标记-清除和分代收集两种机制为辅的策略。

引用计数机制

引用计数法的原理是:每个对象维护一个 ob_refcnt 字段,用来记录该对象当前被引用的次数,每当新的引用指向该对象时,它的引用计数 ob_refcnt 加1,每当该对象的引用失效时计数 ob_refcnt 减1,一旦对象的引用计数为0,该对象立即被回收,对象占用的内存空间将被释放。

导致引用计数 +1 的情况:

对象被创建,例如 a=23

对象被引用,例如 b=a

对象被作为参数,传入到一个函数中,例如 func(a)

对象作为一个元素,存储在容器中,例如 list1=[a,a]

导致引用计数 -1 的情况

对象的别名被显式销毁,例如 del a

对象的别名被赋予新的对象,例如 a=24

一个对象离开它的作用域,例如 func 函数执行完毕时,func 函数中的局部变量(全局变量不会)

对象所在的容器被销毁,或从容器中删除对象

但是引用计数存在一个的严重的问题:循环引用。

循环引用是指:当一个数据结构引用了它自身,即这个数据结构是个循环数据结构,那么它的引用计数值是无法变为零,也就是说这个数据结构占用的内存无法被自动回收。例如:

和 之后已经没有变量指向这个两个列表对象了,而对象的引用计数却没有减少到零。因此使用引用计数法来管理的话,这个两个列表对象永远不会被回收。它会一直驻留在内存中,这样就会造成了内存泄漏。为了解决对象的循环引用问题,Python引入了标记-清除和分代回收两种GC机制。

标记-清除

循环引用的问题只有在容器对象之间才有可能发生,比如列表、字典、类、元组。 首先,为了追踪容器对象,需要每个容器对象维护两个额外的指针, 用来将容器对象组成一个链表,指针分别指向前后两个容器对象,方便插入和删除操作。

标记-清除采用的方法是在不改动真实的引用计数的前提下,将集合中对象的引用计数复制一份副本,改动该对象引用的副本。对于副本做任何的改动,都不会影响到对象生命走起的维护。

第一步,通过计数副本寻找 root object 集合(该集合中的对象是不能被回收的)。例如:aa 和 bb 的循环引用,首先找到循环引用的一端 aa,因为它有一个对 bb 的引用,则将 bb 的引用计数减1;然后顺着引用达到 bb,因为 bb 有一个对 aa 的引用,同样将 aa 的引用减1,这样就完成了循环引用对象间环摘除,并且判断 aa、bb 是不是属于 root object 集合。

第二步,当成功寻找到 root object 集合之后,首先将现在的内存链表一分为二,一条链表中维护 root object 集合,成为 root 链表,而另外一条链表中维护剩下的对象,成为 unreachable 链表。之所以要剖成两个链表,是基于这样的一种考虑:现在的 unreachable 可能存在被root链表中的对象,直接或间接引用的对象,这些对象是不能被回收的(例如:aa 引用了 bb, 而 bb 没有引用 aa),一旦在标记的过程中,发现这样的对象,就将其从 unreachable 链表中移到 root 链表中;当完成标记后,unreachable链表中剩下的所有对象就是名副其实的垃圾对象了,接下来的垃圾回收只需限制在 unreachable 链表中即可。

“标记-清除”执行前需要复制一份副本,这种额外操作实际上与系统中总的内存块的数量是成正相关。当需要回收的内存块越多时,垃圾检测带来的额外操作就越多。

分代回收

为了提高垃圾收集的效率,引入了分代回收,采用“空间换时间的策略”。

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

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20181023G1VJFN00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励