Python - GC
计数引用
- Python 中一切皆对象,变量的本质是对像的指针
- 当一个对象的引用计数(指针数)为 0 时,说明该对象不可达,成为垃圾,需要被回收
- 调用函数
func()
,创建列表 a 后,内存占用迅速增加,在函数调用结束后,内存恢复正常 - 函数内部声明的列表 a 是局部变量,在函数返回后,局部变量的引用会被注销
- 列表 a 所指向的对象的引用数为 0,Python 会执行 GC,回收内存
全局变量 - global
将生成的列表返回,在主程序中接收
内部实现
1 | import sys |
sys.getrefcount
本身也会引入一次计数- 在函数调用发生的时候,会产生两次额外的引用 - 函数栈 + 函数参数
1 | import sys |
手动 GC
1 | import gc |
- del - 删除对象的引用
- gc.collect() - 清除没有引用的对象,手动启动 GC,类似于 Java System.gc()
循环引用
手动 GC
GC 算法
- Python 使用标记清除和分代回收算法,来启用针对循环引用的自动 GC
- 对于一个有向图,从某一个节点出发遍历,标记所经过的所有节点
- 在遍历结束后,所有未被标记的节点,称为不可达节点 - 可被回收
- 遍历全图,非常浪费性能
- 在 Python GC 中,使用双向链表维护一个数据结构
- 并且只考虑容器类的对象,只有容器类的对象才有可能产生循环引用
- Python 将所有对象分为 3 代
- 刚刚创立的对象为第 0 代
- 经历过 1 次 GC 后依然存活的对象,会一次从上一代挪到下一代
- 每一代启动 GC 的阈值,是可以单独设置的
- 当新增对象减去删除对象达到对应的阈值,则会对这一代对象启动 GC
- 思想
- 新生对象更可能被回收,存活更久的对象有更高的概率继续存活 - 节省计算量,提升性能
内存泄露
show_refs() - 生成清晰的引用关系图
1 | import objgraph |
show_backrefs() - 推荐
小结
- GC 是 Python 自带的机制,用于自动释放不再使用的内存空间
- 引用计数是最简单的实现,遇到循环计数时,需要通过不可达判定,来确定是否可以回收
- Python 的自动 GC 算法包括标记清除和分代回收,主要针对循环引用的 GC
All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.