Python 核心技术与实战
景霄
Facebook 资深工程师
114324 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 47 讲
开篇词 (1讲)
Python 核心技术与实战
15
15
1.0x
00:00/00:00
登录|注册

24 | 带你解析 Python 垃圾回收机制

实现一个垃圾回收判定算法
objgraph 是用于调试内存泄漏的工具
Python 使用标记清除和分代收集算法处理垃圾回收
引用计数是一种实现方式,但并非充要条件
垃圾回收是 Python 自带的机制
示例代码演示 objgraph 的使用
使用 objgraph 包进行可视化引用关系分析
示例代码演示循环引用
Python 使用标记清除和分代收集算法处理循环引用
互相引用导致引用计数不为 0
示例代码演示引用计数
对象的引用计数为 0 时触发垃圾回收
思考题
总结
调试内存泄漏
循环引用
计数引用
Python 垃圾回收机制
Python 垃圾回收机制

该思维导图由 AI 生成,仅供参考

你好,我是景霄。
众所周知,我们当代的计算机都是图灵机架构。图灵机架构的本质,就是一条无限长的纸带,对应着我们今天的存储器。在工程学的演化中,逐渐出现了寄存器、易失性存储器(内存)和永久性存储器(硬盘)等产品。其实,这本身来自一个矛盾:速度越快的存储器,单位价格也越昂贵。因此,妥善利用好每一寸高速存储器的空间,永远是系统设计的一个核心。
回到 Python 应用层。
我们知道,Python 程序在运行的时候,需要在内存中开辟出一块空间,用于存放运行时产生的临时变量;计算完成后,再将结果输出到永久性存储器中。如果数据量过大,内存空间管理不善就很容易出现 OOM(out of memory),俗称爆内存,程序可能被操作系统中止。
而对于服务器,这种设计为永不中断的系统来说,内存管理则显得更为重要,不然很容易引发内存泄漏。什么是内存泄漏呢?
这里的泄漏,并不是说你的内存出现了信息安全问题,被恶意程序利用了,而是指程序本身没有设计好,导致程序未能释放已不再使用的内存。
内存泄漏也不是指你的内存在物理上消失了,而是意味着代码在分配了某段内存后,因为设计错误,失去了对这段内存的控制,从而造成了内存的浪费。
那么,Python 又是怎么解决这些问题的?换句话说,对于不会再用到的内存空间,Python 是通过什么机制来回收这些空间的呢?
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

Python的垃圾回收机制是Python内存管理的重要组成部分。本文深入介绍了Python中的引用计数和循环引用两种垃圾回收机制,以及标记清除和分代收集算法的实现方式。文章通过具体的代码示例和清晰的解释,帮助读者深入理解Python的垃圾回收机制,为读者提供了一份全面的Python内存管理指南。此外,文章还介绍了调试内存泄漏的工具objgraph,并提出了一个思考题,挑战读者自己实现一个垃圾回收判定算法。总的来说,本文内容丰富,涵盖了Python内存管理的各个方面,对于想要深入了解Python内存管理的读者来说,是一篇非常有价值的文章。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Python 核心技术与实战》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(29)

  • 最新
  • 精选
  • Jingxiao
    置顶
    思考题答案: 事实上算法可以写的很简单,这是个很经典的 dfs (深度优先搜索)遍历,从起点开始遍历,对遍历到的节点做个记号。遍历完成后,再对所有节点扫一遍,没有被做记号的,就是需要垃圾回收的。
    2019-07-06
    2
    38
  • 陈迪
    1. 循环引用情况下Python不立即回收内存,如果放任不管,即不显式调用gc.collect的话,Python的垃圾回收器自己会什么时候处理? 2. 最后介绍了内存泄露排查工具,哪种算内存泄露呢?接问题1,不立即回收算内存泄露吗?还是有其他场景

    作者回复: 分代收集算法中每一代都有一个默认阈值,超过指定阈值之后就会启动垃圾回收。如果垃圾回收启动太频繁,会造成程序性能低下,分代收集也是为了提高性能,因此不立刻回收没关系,只要一定时间或者一定阈值之后回收都没问题。内存泄漏是这部分内存永远不再被回收,越攒越多,直到撑爆内存。

    2019-07-03
    4
    23
  • 天凉好个秋
    本文讲的垃圾回收算法在Java中也都有,当初在设计的时候是不是参考了Java?而且,Java中还有标记整理算法,可以解决回收内存不连续的问题,这个在Python中有没有考虑呢?

    作者回复: 程序语言设计的时候肯定会有互相参考,Java 中的 gc 就很丰富了,程序员可以根据需要配置适合自己应用的 gc 算法,甚至在 OpenJDK 开源后,可以在更深的层次上对本来不对用户开放的细节进行深入配置。python 则并不希望编写者们对这里有过深入了解,拿来开箱即用就行了,这也是 python 设计哲学的一部分,牺牲一定性能换取方便性。

    2019-07-03
    3
    9
  •   星豪
    1. 在读文章的时候找了一个可能是错别字的地方,在循环引用那一节中,第四段试想一下,如果这段代码出现在生产环境中...但经过长时间运行“候”...。这一侯应该是后来的后吧? 2. 当垃圾回收器中新增对象减去删除对象达到相应的阈值时,就会对这一代对象启动垃圾回收。这一句话不是很明白,新增对象我理解的是新创建的对象或者是从上一代挪过来的对象,那么删除对象指的是哪些呢?或者说是如何进行指定哪些是应该被删除的对象呢?

    作者回复: 1. 谢谢 2. 新增对象指代你创建一个对象,删除对象指代对象被释放,例如手动调用 del,从函数中返回临时变量的释放等,对这两者进行计数统计,然后相减。

    2019-07-04
    5
  • MirkoWei
    windows下使用objgraph遇到个问题: `failed to execute ['dot', '-Tpdf'], make sure the Graphviz executables are on your systems' path` 安装objgraph的时候,需要的前置条件graphviz、xdot都安装了,系统环境变量也添加了,仍然找不到路径 之后通过stackoverflow得到解决办法,就是每次使用的时候,需要在代码前面手动添加环境变量 ``` import os os.environ["PATH"] += os.pathsep + 'xxx/Graphviz2.38/bin/' ``` 问题是解决了,但是每次都需要手动添加环境变量也太麻烦了,不知道是否有更好的解决办法

    作者回复: 很建议从一开始就尝试用 linux 学习编程,而不是花费大量时间解决 windows 下环境配置的问题。

    2020-05-19
    3
  • 王坤祥
    课程越往后越有意思,发现了很多知识点盲区,这门课很值!

    作者回复: 加油!

    2020-07-02
    1
  • Switch
    思考题: from typing import Set class Graph: def __init__(self, value, nodes=None): self._value = value self._nodes: list = [] if nodes is None else nodes @property def value(self): return self._value @property def nodes(self): return self._nodes def node_add(self, node): self._nodes.append(node) def node_adds(self, nodes): self._nodes.extend(nodes) def node_del(self, node): self._nodes.remove(node) def __str__(self): return "Graph {} nodes {}".format(self._value, [node.value for node in self.nodes]) def __repr__(self): return self.__str__() def dfs(start: Graph, includes: Set[Graph] = None) -> Set[Graph]: if includes is None: includes = set() if start in includes: return includes includes.add(start) for s in start.nodes: includes.update(dfs(s, includes)) return includes if __name__ == '__main__': A = Graph('A') B = Graph('B') C = Graph('C') D = Graph('D') E = Graph('E') F = Graph('F') has_nodes = {A, B, C, D, E, F} # A->B->E # ->C->E # B->A # D->F # F->D A.node_adds([B, C]) B.node_adds([A, E]) C.node_adds([E]) D.node_adds([F]) F.node_adds([D]) graph_nodes = dfs(A, set()) # OUT: {Graph B nodes ['A', 'E'], Graph E nodes [], Graph C nodes ['E'], Graph A nodes ['B', 'C']} print(graph_nodes) # OUT: {Graph F nodes ['D'], Graph D nodes ['F']} print(has_nodes - graph_nodes)
    2019-07-08
    1
    23
  • youaresherlock
    四次引用,a,python 的函数调用栈,函数参数,和 getrefcount 不理解这里的函数调用栈、函数参数为什么增加了2次,这里有什么区别?他们两个不是一样的吗,函数参数在函数调用栈里,应该是一次啊
    2020-07-06
    1
    8
  • 你说呢
    可以这样理解么:python的垃圾回收机制,以引用计数算法为主、标记-删除算法为辅 来确定内存中哪些对象可以回收;而分代回收算法确定了垃圾是什么时候被回收。
    2021-03-17
    5
  • 程序员人生
    请问一下,老师 执行关于objgraph代码,出现如下错误: Graph viewer (xdot) and image renderer (dot) not found, not doing anything else 是不是还要安装什么软件?
    2019-07-03
    10
    5
收起评论
显示
设置
留言
29
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部