垃圾回收
gc机制,garbage collection(垃圾回收)
现在的高级语言如java,c#等,都采用了垃圾收集机制。而不再是c,c++里用户自己管理维护内存的方式。自己管理内存极其自由,可以任意申请内存,但如同一把双刃剑,为大量内存泄露,悬空指针等bug埋下隐患。
python里也同java一样采用了垃圾收集机制,不过不一样的是python采用的是引用计数机制为主,标记-清除和分代收集两种机制为辅的策略
如果我们的程序一直在创建而没有释放,就会导致程序运行期间,内存越来越大,越来越大
就是说垃圾回收不用你考虑,python中的内存,一定意义上解放了程序员的工作
你创建一个对象以及对象的销毁回收都不用我们管。而像C语言,C++就需要我们自己管,你创建的对象,你自己负责回收,你要是不回收,它就一直在那。而python有垃圾回收机制,你创建一个对象,什么时候不用了,它就回收了,当然这个回收不具备及时性,有一个延迟
垃圾回收机制
- 引用计数机制
- 隔代回收机制
引用计数机制
每个对象是否应该销毁,有一个标识来记录。记录当前有几个人在用它,当这个计数为1的时候,就可以回收了
看一下源码
python是C语言开发的,python里每一个东西都是对象,它们的核心就是一个结构体:PyObject
PyObject是每个对象必有的内容,其中ob_refcnt就是做为引用计数。当一个对象有新的引用时,它 的ob_refcnt就会增加,当引用它的对象被删除,它的ob_refcnt就会减少
当引用计数为1时,该对象生命就结束了
python3里面是默认开启垃圾回收机制的,还可以手动关闭它
del 对象
其实不是直接删除对象,而是减少一个引用计数。这个对象删不删是由gc机制来决定的
import gc import sys class AAA(object): def __init__(self): print ("object: born at:%s"%hex(id(self))) def __new__(cls, *args, **kwargs): #创建对象的时候会自动调用它 print ("new") return super(AAA, cls).__new__(cls) def __del__(self): #删除的时候,内存被销毁的时候会自动调用它 print ("bye bye") a = AAA() print(sys.getrefcount(a))
刚出生的时候引用计数是2
然后所有对象在程序结束的时候都会被释放
import gc import sys class AAA(object): def __init__(self): print ("object: born at:%s"%hex(id(self))) def __new__(cls, *args, **kwargs): #创建对象的时候会自动调用它 print ("new") return super(AAA, cls).__new__(cls) def __del__(self): #删除的时候,内存被销毁的时候会自动调用它 print ("bye bye") a = AAA() print(sys.getrefcount(a)) list1 = [] list1.append(a) print(sys.getrefcount(a))
可以看到,list1中加入a之后,对a的引用变3了
import gc import sys class AAA(object): def __init__(self): print ("object: born at:%s"%hex(id(self))) def __new__(cls, *args, **kwargs): #创建对象的时候会自动调用它 print ("new") return super(AAA, cls).__new__(cls) def __del__(self): #删除的时候,内存被销毁的时候会自动调用它 print ("bye bye") a = AAA() print(sys.getrefcount(a)) list1 = [] list1.append(a) print(sys.getrefcount(a)) list1.remove(a) print(sys.getrefcount(a))
又变2了
引用计数机制的优点
1.简单
2.实时性:一旦没有引用,内存就直接释放了。不用像其他机制等到特定时机。
实时性还带来一个好处:处理回收内存的时间分摊到了平时。引用计数机制的缺点:
1.维护引用计数消耗资源
2.循环引用的问题无法解决(DOS窗口,查看内存tasklist,或者内存表,任务管理器)gc模块的使用
import gc
隔代回收机制
隔代回收是用来解决交叉引用(循环引用),并增加数据回收的效率.
原理: 通过对象存在的时间不同,采用不同的算法来 回收垃圾.
形象的比喻,三个链表,零代链表上的对象(新创建的对象都加入到零代链表),引用数都是一,每增加一个指针,引用加一,随后python会检测列表中的互相引用的对象,根据规则减掉其引用计数.
GC算法对链表一的引用减一,引用为0的,清除,不为0的到链表二,链表二也执行GC算法,链表三一样.存在时间越长的数据,越是有用的数据
隔代回收触发时间(GC阈yu值)
随着你的程序运行,Python解释器保持对新创建的对象,以及因为引用计数为零而被释放掉的对象的追踪。从理论上说,这两个值应该保持一致,因为程序新建的每个对象都应该最终被释放掉。
当然,事实并非如此。因为循环引用的原因,从而被分配对象的计数值与被释放对象的计数值之间的差异在逐渐增长。一旦这个差异累计超过某个阈值,则Python的收集机制就启动了,并且触发上边所说到的零代算法,释放“浮动的垃圾”,并且将剩下的对象移动到一代列表。
随着时间的推移,程序所使用的对象逐渐从零代列表移动到一代列表。而Python对于一代列表中对象的处理遵循同样的方法,一旦被分配计数值与被释放计数值累计到达一定阈值,Python会将剩下的活跃对象移动到二代列表。
通过这种方法,你的代码所长期使用的对象,那些你的代码持续访问的活跃对象,会从零代链表转移到一代再转移到二代。通过不同的阈值设置,Python可以在不同的时间间隔处理这些对象。Python处理零代最为频繁,其次是一代然后才是二代
就是隔段时间筛一次
import gc import os class AAA(object): def __init__(self): print ("object: born at:%s"%hex(id(self))) def __new__(cls, *args, **kwargs): #创建对象的时候会自动调用它 print ("new") return super(AAA, cls).__new__(cls) def __del__(self): #删除的时候,内存被销毁的时候会自动调用它 print ("bye bye") def start(): while True: a = AAA() b = AAA() #给a添加成员变量 a.v = b #给b添加成员变量 b.v = a #无法删除,只是减少引用计数 del a del b #关闭垃圾回收机制 gc.disable() start()
import gc import os class AAA(object): def __init__(self): print ("object: born at:%s"%hex(id(self))) def __new__(cls, *args, **kwargs): #创建对象的时候会自动调用它 print ("new") return super(AAA, cls).__new__(cls) def __del__(self): #删除的时候,内存被销毁的时候会自动调用它 print ("bye bye") def start(): while True: a = AAA() b = AAA() #给a添加成员变量 a.v = b #给b添加成员变量 b.v = a #无法删除,只是减少引用计数 del a del b print(gc.get_count()) print(gc.get_threshold()) #关闭垃圾回收机制 gc.disable() start()
程序执行的时候创建的对象会逐渐增多
此时因为我们把垃圾机制关掉了,就不回收了,所以对象能累计到1w+
当我们打开垃圾回收机制
到了700就开始回收了
手动回收
import gc import os class AAA(object): def __init__(self): print ("object: born at:%s"%hex(id(self))) def __new__(cls, *args, **kwargs): #创建对象的时候会自动调用它 print ("new") return super(AAA, cls).__new__(cls) def __del__(self): #删除的时候,内存被销毁的时候会自动调用它 print ("bye bye") def start(): while True: a = AAA() b = AAA() #给a添加成员变量 a.v = b #给b添加成员变量 b.v = a #无法删除,只是减少引用计数 del a del b print(gc.get_count()) print(gc.get_threshold()) #关闭垃圾回收机制 #gc.disable() #手动回收 gc.collect() start()
之前后两个都不是0,因为调用了手动回收,所以现在是0了
默认阈值是(700,10,10),也可以手动设置
import gc import os class AAA(object): def __init__(self): print ("object: born at:%s"%hex(id(self))) def __new__(cls, *args, **kwargs): #创建对象的时候会自动调用它 print ("new") return super(AAA, cls).__new__(cls) def __del__(self): #删除的时候,内存被销毁的时候会自动调用它 print ("bye bye") def start(): while True: a = AAA() b = AAA() #给a添加成员变量 a.v = b #给b添加成员变量 b.v = a #无法删除,只是减少引用计数 del a del b print(gc.get_count()) print(gc.get_threshold()) #关闭垃圾回收机制 #gc.disable() #手动回收 #gc.collect() gc.set_threshold(100,5,5) start()
到100就开始回收了
当然你在做这些的前提是把垃圾回收机制给关了,不然根本看不出来的