本文目录一览:
python垃圾回收机制(超详细)
概述:引用计数为主,标记清除,分代回收为辅
1引用计数
python程序中创建的所有的对象都是放在一个双向环状循环链表refchain上的
如下对象被创建时,在C语言底层实际结构
name='string'
c语言内底部创建成 [上一个对象,下一个对象,类型,引用个数]
age=18
c语言内底部创建成[上一个对象,下一个对象,类型,引用个数,val=18]
hobby=['篮球', '撸铁',‘玩’]
c语言内底部创建成[上一个对象,下一个对象,类型,引用个数,item=元素, 元素个数]
当python程序运行时,会根据数据类型的不同找到其对应的结构体,根据结构体中的字段来进行创建相关的数据,然后将对象添加到refchain双向链表中
每个对象中有ob_refcnt就是应用计数器,默认为1,当有其他变量引用对象时,引用计数器就会+1
当引用计数器为0时,意味着没人使用这个对象了,这个对象就是垃圾,就会回收
回收步骤 :1对象从refchain链表移除 2将对象销毁,内存回收
2 标记清除
为什么要标记清除 :为了解决引用计数器循环引用的不足,循环引用可能导致内存泄漏
实现:在python的底层,再维护一个链表,链表中专门放那些可能存在循环引用的对象(list/tuple/dict/set)
在python内部,某种情况下触发,回去扫描可能存在循环引用链表中的每个元素,检查是否是循环引用,如果有,则让双方的引用计数器-1,如果是0,则垃圾回收
3 分代回收
为什么要分代回收: 不知道什么情况下触发扫描,可能存在循环引用的链表扫描代价大,每次扫描很久
将可能存在循环引用的对象维护成3个链表
0代:0代中对象个数达到700个扫描一次
1代:0代扫描10次,则1代扫描1次
2代:1代扫描10次,则2代扫描1次
过程:当我们创建了一个对象a=1,这个对象只会加到refchain链表中,而当我们创建了一个可能存在循环引用的对象b=[]一个列表时,这个对象不但会加到refchain链表中,还会加到分带回收的0代链表中,当0代链表中对象达到700个,GC开始扫描,如果是循环引用,那就自减1,减完以后,如果是垃圾,那就自动回收,如果不是垃圾,那就将这些对象升级到1代链表中,就这样扫描一遍,此时0代链表也会记录自己扫描了1次,等到下次0代链表的对象又达到了700个,继续上述步骤,就这样执行了10次,才会触发执行扫描1代链表,1代链表和2代链表中的操作和0代中一样。
4 小结(面试可以这么说)
在python中,维护了一个refchain的双向循环环状链表,这个链表中存储程序创建的所有对象,每种类型的对象中都有一个0b_refcnt引用计数器的值,默认为1,当引用计数器变为0时会进行垃圾回收(对象销毁,refchain中移除)
但是,在python 中,对于那些可以有多个元素组成的对象可能会存在循环引用的问题,为了解决这个问题,python又引入了标记清除和分代回收,在其内部维护了四个链表
refchain
0代 700个对象触发
1代 0代十次执行一次1代
2代 1代十次执行一次2代
当 每个链表达到阈值时,就会触发扫描链表进行标记清除操作,有循环则各自-1,为0时,直接回收,销毁,清除
But, 在上面的垃圾回收机制的步骤中,python提供了优化机制
缓存
小整数对象池
为了避免重复创建和销毁一些常用对象,维护了一个小整数对象池
-5~257的地址内存是一定的,这些对象是pyhton事先帮我们创建好了
free_list(会有大小限制)
当一个对象的引用计数为0时,按理说应该回收,但是python没有回收,而是把这个对象放到了一个free_list中当缓存,以后再去使用时,不在重新开辟内存,而是直接使用free_list
比如现在一个对象V=3.14 ,我现在把他del V, 代表引用计数为0 了,但是这块地址我不会回收,而是放到free_list中,然后我又创建了一个新的对象v1=999,这个对象不会开辟一块新内存,而是直接从free_list中去获取对象,然后把对象内部的数据进行初始化成999,再放到refchain中去,需要注意的是,free_list有大小限制,如果free_list链表满了,当一个对象的引用计数为0时,会直接回收这块地址,而不会放到free_list中进行缓存
float: 维护了free_list长度为100
int:不是基于free_list, 而是维护一个small_list保持常见的数据(小数据池),重复使用不会开辟新的内存
str: 内存将所有的ascii字符缓存起来,以后使用的时候不会反复创建
list: 维护了free_list长度为80
tuple:根据元素个数来维护free_list长度
dict:维护了free_list长度为80
Python垃圾回收机制是什么样的?
Python垃圾回收机制是通过引用计数来管理的引用计数表示记录这个对象被引用的次数如果有新的引用指向对象,对象引用计数就加一,引用被销毁时,对象引用计数减一,当用户的引用计数为0时,该内存被释放以上就是Python的垃圾回收机制了 ,在黑马程序员看过一个视频,有专门讲解的,你可以去看看!谢谢你,如果你有这方面的问题的话,您可以随时询问我
python垃圾收集机制?
不再使用的内存会被一种称为垃圾收集的机制释放。像上面说的,虽然解释器跟踪对象的引用计数,但垃圾收集器负责释放内存。垃圾收集器是一块独立代码,它用来寻找引用计数为0的对象。它也负责检查那些虽然引用计数大于0但也应该被销毁的对象。特定情形会导致循环引用。一个循环引用发生在当你有至少两个对象互相引用时,也就是说所有的引用都消失时,这些引用仍然存在,这说明只靠引用计数是不够的。Python的垃圾收集器实际上是一个引用计数器和一个循环垃圾收集器。当一个对象的引用计数变为0,解释器会暂停,释放掉这个对象和仅有这个对象可访问(可到达)的其他对象。作为引用计数的补充,垃圾收集器也会留心被分配的总量很大的(及未通过引用计数销毁的那些)对象。在这种情况下,解释器会暂停下来,试图清理所有未引用的循环。
Python的垃圾回收机制原理
1.Python的垃圾回收机制原理
Python无需我们手动回收内存,它的垃圾回收是如何实现的呢?
引用计数为主(缺点:循环引用无法解决)
引入标记清除和分代回收解决引用计数问题
引用计数为主+标记清除和分代回收为辅
垃圾回收(GC)
(1)引用计数
a = [1] # [1]对象引用计数增加1,ref=1
b = a # [1]对象引用计数增加1,ref=2
b = None # [1]对象引用计数减少1,ref=1
del a # [1]对象引用计数减少1,ref=0
a = [1],当把列表 [1] 赋值给 a 的时候,它的引用计数就会增加1,此时列表 [1] 对象的引用计数ref=1 ; b = a 又把 a 赋值给 b ,a和b 同时引用了列表[1]对象,ref又增加1,此时 ref =2。继续执行 b = None, 让b指向None,这个时候它就不会指向原来的列表[1]对象,列表[1]对象的引入计数就会减少1,又变成ref=1。执行del a ,引用计数就会减少1,这个时候 ref = 0。当对象的引用计数为0就可以回收掉,
注意:del 作用就会减少对象引用计数,并不是销毁对象。只有当引用计数为0的时候,Python解释器才回去把对象占用的内存回收掉。
// object.h
struct _object {
Py_ssize_t ob_refcnt; # 引用计数值
}PyObject;
① 什么时候引用计数增加呢?
② 什么时候引用计数会减少呢?
(2)引用计数无法解决循环引用问题
循环引用
a = [1] # 对象[1]引用计数增加1,ref=1
b = [2] # 对象[2]引用计数增加1,ref=1
a.append(b) # b被a引用,对象[2]引用计数增加1,ref=2
b.append(a) # a又被b引用,对象[1]引用计数增加1,ref=2
del a # 对象[1]引用计数减少1,ref=1
del b # 对象[2]引用计数减少1,ref=1
(3)标记清除(Mark and Sweep)
(4)分代回收
import gc
python中的变量与垃圾回收
python中的变量和java中的变量本质是不一样的,python中的变量实质上是一个指针(指针的大小固定的)
is可以用来判断id是否相等
对于这种赋值,虽然所赋值是相同的,但是他们的id不同,即他们是不同的对象,a is b 即为false ,但是有个特例: a = 1 b = 1 时他们的id相同。其实这是python内部的优化机制,对于小整数和小的字符串来说,python在前边定义一个对象时,下次在遇到时会直接调用前边生成的对象,而不会去重新申请一个。
他们的对象内存地址不一样,但是,a和b里的值是相等的,这是由于a和b都为list,而list里有内置的魔法函数 eq 通过 eq 魔法函数可以判断里边两个的值是否相同,若相同则返回True
python中垃圾回收的算法回收的算法是采用引用计数,当程序中有一个变量引用该python对象时,python会自动保证该对象引用计数为1;当程序中有两个变量引用该python对象时,python会自动保证该对象计数器为2, 以此类推,当一个对象的引用计数器变为0 时,则说明程序中不再有变量对其进行引用,因此python就会回收该对象。
大多数情况,python的ARC都能准确,高效的回收系统中的每一个对象。但如果系统中出现循环引用时,比如a对象持有一个实例变量引用对象b,而b对象又持有一个实例变量引用对象a,此时 两个对象的计数器都为1, 而实际上python不再需要这两个对象,也没有程序在引用他们,系统回收他们时python的垃圾回收器就没有那儿快,要等到专门的循环垃圾回收器(Cyclic Garbage Collector)来检测并回收这种引用循环
当一个对象被垃圾回收式,python就会自动调用该对象的 del 方法
当没有注释掉x = im时, item对象被两个变量所引用,所以在执行完del im时并不会去回收item对象,所以先输出--------,当程序完全执行完成后,引用item的对象的变量被释放,然后系统便会执行 del 方法,回收item对象。
当 x = im被注释后,只有一个变量去引用item对象,所以在执行完后程序变回去调用 del 方法,回收item对象,然后在继续向下执行 输出-----