垃圾回收(Garbage Collection)
1. 找到内存中无用的垃圾资源
2. 清除这些垃圾并把内存让出来给其他对象使用。
python中的垃圾回收:
主要手段:引用计数
辅助手段:标记清除和分代回收机制
【引用计数】
每个对象都有PyObject,当对象有新的引用,ob_refcnt 增加;当引用被删除,ob_refcnt 就减少;当引用计数=0, 该对象生命就结束了
1. 导致引用计数+1的情况有哪些
对象被创建,被一个变量引用,赋值操作:a = 8
对象被另一个变量引用,赋值操作:b = a
对象作为参数传递给函数:func(a)
对象作为元素存储到容器中,比如:添加到列表、元组、字典、集合中:tmp = [a]
2. 导致引用计数-1的情况有哪些
引用对象的变量被删除: del a
引用这个对象的变量指向其他对象:a = "abc"
函数作用域执行完毕,函数执行结束,函数中的局部变量就会消失
对象所在的容器被销毁,或者从容器中删除了这个对象
【标记清除】
标记清除(Mark—Sweep)算法是一种基于追踪回收(tracing GC)技术实现的垃圾回收算法。
它分为两个阶段:第一阶段是标记阶段,GC会标记 所有的活动对象,第二阶段是回收 那些没有标记的非活动对象。
对象之间通过引用(指针)连在一起,构成一个有向图,对象是这个有向图的节点,而引用关系是这个有向图的边。
从根对象(root object)出发,沿着有向边遍历对象,可达的(reachable)对象标记为活动对象,不可达的对象就是要被清除的非活动对象。
根对象就是全局变量、调用栈、寄存器。
适用:容器对象,如:list、dict、tuple等,因为str、int不可能造成循环引用
缺点:需要顺序扫描整个堆内存
【分代回收】
建立在标记清除技术上,以空间换时间
Python将内存,根据对象的存活时间划分为不同集合,每个集合称为一个代,即:年轻代(0)、中年代(1)、老年代(2)
对应三个链表,垃圾回收频率,随着对象的存活时间的增大而减小
新创建的对象被分配在年轻代,年轻代链表达到上线时,触发垃圾回收机制,回收那些可以被回收的对象,不会被回收的对象会被移动到中年代
以此类推,老年代中的对象,存活时间最久,甚至于存活于整个系统的生命周期内
【gc模块常用函数有哪些】
1、gc.set_debug(flags) 设置gc的debug日志,
一般设置为gc.DEBUG_LEAK,可以看到内存泄露的对象
2、gc.collect([generation]) 显式执行垃圾回收,回收循环引用的对象。
入参:
0:只回收第0代对象,
1:回收0和1代对象,
2:回收0、1和2代对象,
如果不传参数,默认值为2。
返回:不可达(unreachable objects)对象的数量
3、gc.get_threshold() 获取gc模块自动执行垃圾回收的阈值/频率。
返回:元组,分别为:第0、1和2代的阈值
举例:gc.get_threshold()返回值为:(700, 10, 10),则:
0代垃圾值到了700,就会执行gc.collect(0),回收0代的垃圾
1代垃圾值到了710,就会执行gc.collect(1),回收0和1代的垃圾
0代垃圾值到了700,就会执行gc.collect(2),回收0、1和2代的垃圾
4、gc.set_threshold(threshold[0], threshold[1], threshold[2]) 设置自动执行垃圾回收的阈值/频率。
5、gc.get_count() 获取当前自动执行垃圾回收的计数器,
返回:元组,长度为3,分别为:第0代垃圾对象的数量、等0代链表遍历的次数,第1代链表遍历的次数
注意点:
gc模块不能处理的是,如果两个循环引用的对象都实现了__del__方法,那么将不会进行垃圾回收,因此尽量不要在类中实现自己的__del__方法。否则发生循环引用后就会产生内存泄露。
# 怎样查看一个对象的引用计数
# 用sys.getrefcount(a),举例如下:
import sys
a = 1
print(sys.getrefcount(a)) # 200
b = a
print(sys.getrefcount(a)) # 201
del b
print(sys.getrefcount(a)) # 200
# 注:调用sys.getrefcount()时,对象的引用会+1
refer:https://blog.csdn.net/zhoulei124/article/details/89021892
Python 垃圾回收
最新推荐文章于 2024-04-29 02:34:11 发布