垃圾回收机制

Python GC主要使用引用计数来跟踪和回收垃圾。在引用计数的基础上,通过标记-清除解决容器对象可能产生的循环引用问题,通过分代回收以空间换时间的方法提高垃圾回收效率。

Python采用的是引用计数机制为主,标记-清除和分代收集两种机制为辅的策略。

引用计数

PyObject是每个对象必有的内容,其中ob_refcnt就是引用计数。当一个对象有新的引用时,它的ob_refcnt就会增加,当引用它的对象被删除,它的ob_refcnt就会减少。引用计数为0时,该对象生命就结束了。

通过使用内置的模块sys.getrefcount()可以查看对象的引用计数,但是会比正常计数大1。因为调用函数的时候需要传入对象使得其引用计数增加1。

  • 增加引用计数:对象被创建、对象被引用、对象被作为参数传入到一个函数中、对象作为一个元素存储在容器中

  • 减少引用计数:对象的别名被显示销毁、对象的别名被赋予新的对象、一个对象离开了它的作用域、对象所在的容器被销毁后从容器中删除对象

一个误区:Python系统维护着一个常见的整数常量池,即-5~255,在这个区间的数字会有其他的处理方式。包括字符串也有一些特殊的处理,所以在使用应用技术的时候,最好是使用自己自定义的数据类型,这样方便分析。

import sys

nu = 10
print(sys.getrefcount(nu)-1)    # 16
import sys

class A:
    pass

a = A()
print(sys.getrefcount(a)-1) # 1
b = a
print(sys.getrefcount(a)-1, sys.getrefcount(b)-1)   # 2 2

del b
print(sys.getrefcount(a)-1) # 1

引用计数的致命缺陷:循环引用导致的内存泄漏

内存泄漏是指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误失去了对该段内存的控制,因而造成了内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。

导致内存泄漏的主凶是有__del__()函数的对象之间的循环引用。在不使用一个对象的时候,应使用del object 来删除一个对象的引用计数,这样就可以有效地防止内存泄漏的问题。

通过Python扩展模块gc来查看不能回收的对象的详细信息。

import sys

lstOne = []
lstTwo = []
lstOne.append(lstTwo)
lstTwo.append(lstOne)

print(sys.getrefcount(lstOne), sys.getrefcount(lstTwo)) # 3 3

lstOne.remove(lstTwo)

print(sys.getrefcount(lstOne), sys.getrefcount(lstTwo)) # 3 2

标记-清除机制

基本思路:先按需分配,等到没有空闲内存的时候从寄存器和程序栈上的引用出发,遍历以对象为节点、以引用为边构成的图,把所有可以访问到的对象打上标记,然后清扫一遍内存空间,把所有没标记的对象释放。

标记清除算法是一种基于追踪回收技术实现的垃圾回收算法。它分为两个阶段。

  • 标记阶段:GC会把所有的活动对象打上标记

  • 回收阶段:把那些没有标记的对象非活动对象进行回收

对象之间通过引用(指针)连在一起,构成一个有向图,其中对象构成这个有向图的节点,而引用关系构成这个有向图的边。从根对象出发,沿着有向边遍历对象,可达的对象标记为活动对象,不可达的对象就是要被清除的非活动对象。根对象就是全局变量、调用栈、寄存器。

作为Python的辅助垃圾收集技术,标记清除算法主要处理的是一些容器对象,比如list、dict、tuple,instance等,因为对于字符串、数值对象是不可能造成循环引用问题。Python使用一个双向链表将这些容器对象组织起来。

缺点:清除非活动的对象前必须顺序扫描整个堆内存,哪怕只剩下小部分活动对象也要扫描所有对象。

分代技术

整体思想:将系统中的所有内存块根据其存活时间划分为不同的集合,每个集合就成为一个,垃圾收集的频率随着的存活时间增大而减小,存活时间通常利用经过几次垃圾回收来度量。

Python默认定义了三代对象集合,索引数越大,对象存活时间越长。

分代技术是一种典型的以空间换时间的技术,简单点说就是:对象存在时间越长,越可能不是垃圾,应该越少去收集。
分代就是将回收对象分成数个,每个就是一个链表(集合),进行标记-清除的时间与内对象
存活时间成正比例关系。

Python里一共有三,每个的threshold值表示该最多容纳对象的个数。默认情况下,当0代超过700,或1、2代超过10,垃圾回收机制将触发。0代触发将清理所有三1代触发会清理1、2代2代触发后只会清理自己。

分代回收是建立在标记-清除技术基础之上,分代回收同样作为Python的辅助垃圾收集技术处理那些容器对象。

垃圾回收与性能调优

手动垃圾回收、调高垃圾回收阈值、避免循环引用(手动解循环引用和使用弱引用)

(最近更新:2019年04月11日)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值