python里每一个东西都是对象,它们的核心就是一个结构体:PyObject
PyObject是每个对象必有的内容,其中ob_refcnt就是做为引用计数。当一个对象有新的引用时,它的ob_refcnt就会增加,当引用它的变量被删除,它的ob_refcnt就会减少
优点
1.简单
2.实时,一旦没有引用,内存就直接释放了。不用像其他机制等到特定时机。实时性还带来一个好处,处理回收内存的时间分摊到了运行时。
缺点
1.维护引用计数消耗资源,会造成循环引用导致无法回收,造成内存泄露
2.循环引用
list1 = []
list2 = []
list1.append(list2)
list2.append(list1)
list1与list2相互引用,如果不存在其他对象对它们的引用,list1与list2的引用计数也仍然为1,所占用的内存永远无法被回收,这将是致命的。
在Python中,所有能够引用其他对象的对象都被称为容器(container)。因此只有容器之间才可能形成循环引用。Python的垃圾回收机制利用了这个特点来寻找需要被释放的对象。为了记录下所有的容器对象,Python将每一个 容器都链到了一个双向链表中,之所以使用双向链表是为了方便快速的在容器集合中插入和删除对象。有了这个维护了所有容器对象的双向链表以后, Python在垃圾回收时使用如下步骤来寻找需要释放的对象:
1.对于每一个容器对象, 设置一个gc_refs值, 并将其初始化为该对象的引用计数值.
2.对于每一个容器对象, 找到所有其引用的对象, 将被引用对象的gc_refs值减1.
3.执行完步骤2以后所有gc_refs值还大于0的对象都被非容器对象引用着, 至少存在一个非循环引用. 因此不能释放这些对象, 将他们放入另一个集合.
4.在步骤3中不能被释放的对象, 如果他们引用着某个对象, 被引用的对象也是不能被释放的, 因此将这些 对象也放入另一个集合中.
5.此时还剩下的对象都是无法到达的对象. 现在可以释放这些对象了.
分代技术简单点说就是:对象存在时间越长,越可能不是垃圾,应该越少去收集。
1.Python将所有对象根据’生存时间’分为3代, 从0到2.
2.所有新创建的对象都分配为第0代.
3.当这些对象经过一次垃圾回收仍然存在则会被放入第1代中. 如果第1代中的对象在一次垃圾回收之后仍然存在则被放入第2代.
4.对于不同代的对象Python的回收的频率也不一样. 可以通过gc.set_threshold(threshold0[, threshold1[, threshold2]]) 来定义
5.当Python的垃圾回收器中新增的对象数量减去删除的对象数量大于threshold0时, Python会对第0代对象 执行一次垃圾回收. 每当第0代被检查的次数超过了threshold1时, 第1代对象就会被执行一次垃圾回收. 同理每当第1代被检查的次数超过了threshold2时, 第2代对象也会被执行一次垃圾回收.
在三种情况下会触发垃圾回收:
1.达到了垃圾回收的阈值,Python虚拟机自动执行
2.手动调用gc.collect()
3.Python虚拟机退出的时候