Python
中的垃圾回收主要以引用计数为主,分代回收为辅.
引用计数的原理是每个对象维护一个ob_ref
,用来记录当前对象被引用的次数.
一般在以下情形引用计数为增加
foo(x)
li = [1,x,‘33‘]
一般会在以下情形引用计数减少:
一个对象引用离开了它的作用域 比如 foo(x)
函数执行完成
对象的别名被显式销毁,如del x
或者del y
对象的别名会被赋值给其他对象 x = 78
对象从容器对象被移除 li.remove(x)
对象所在的容器被销毁 del li
当引用计数为0的时候,该对象生命就会结束.
维护引用计数,会消耗资源.
无法解决循环饮用的问题,即两个对象相互引用,且没有外部引用的时候,应当被清除,但是他们的引用计数都为1,而不会被清除.
list1 = []
list2 = []
list1.append(list2)
list2.append(list1)
为了解决上面的缺陷,Python 中由引入两种GC机制(垃圾回收机制),分别为标记-清除
和分代回收
Python的引用计数算法不能处理相互指向自己的对象,且Python代码会在无意间包含循环引用,Python的运行程序会建立一定数量的‘浮点数垃圾‘,Python的GC不能引用计数为1且未使用的对象,因此引入标记-清除
算法.
标记清除算法是一种基于追踪回收技术实现的方法,分为标记阶段和回收阶段两个阶段.标记阶段是将活动对象打上标记,回收阶段是将没有打上标记的对象(非活动对象)进行回收.
如何判断是否是活动对象
对象之间通过引用连接在一起,构成一个有向图,对象构成这个有向图的节点,引用关系构成有向图的边.从根对象开始沿着边进行遍历对象,可达的对象标记为活动对象,不可达的对象标记为非活动对象.
一般而言,根对象就是全局变量,调用栈或者寄存器.
在Python中,标记清除算法作为Python的辅助垃圾回收机制,主要用于处理一些容器对象,如list
,dict
,tuple
,instance
.
Python 使用的是双向链表将这些对象组织起来的.
清除非活动对象的时候,他必须顺序扫描整个堆内存,哪怕只剩下很小部分的活动对象,依然会扫描所有对象.会很耗时.
分代回收同样作为Python的辅助垃圾收集技术处理那些容器对象.
分代回收的逻辑
分配内存
-> 发现超过阈值了
-> 触发垃圾回收
-> 将所有可收集对象链表放到一起
-> 遍历, 计算有效引用计数
-> 分成 有效引用计数=0 和 有效引用计数 > 0 两个集合
-> 大于0的, 放入到更老一代
-> =0的, 执行回收
-> 回收遍历容器内的各个元素, 减掉对应元素引用计数(破掉循环引用)
-> 执行-1的逻辑, 若发现对象引用计数=0, 触发内存回收
-> python底层内存管理机制回收内存
分代回收是一种以空间换时间的操作方式.
Python将内存根据对象的存活时间划分为不同的集合,每个集合称为一个代,Python将内存分为了3“代”,分别为年轻代(第0代)、中年代(第1代)、老年代(第2代),他们对应的是3个链表,它们的垃圾收集频率与对象的存活时间的增大而减小。
新创建的对象都会分配在年轻代,年轻代链表的总数达到上限时,Python垃圾收集机制就会被触发,把那些可以被回收的对象回收掉,而那些不会回收的对象就会被移到中年代去,依此类推,老年代中的对象是存活时间最久的对象,甚至是存活于整个系统的生命周期内。
同时,分代回收是建立在标记清除技术基础之上。分代回收同样作为Python的辅助垃圾收集技术处理那些容器对象.
原文:https://www.cnblogs.com/Jasaja/p/13723547.html