从内存泄露聊聊 python 内存管理
在使用 cpython 时, 发现偶尔会发生内存泄露。这是什么原因呢?
从 python 内存管理机制开始说起
默认的内存分配器
python 中所有内存管理机制都有两套实现,通过编译符号
PYMALLOC_DEBUG
控制,在 debug 模式下可以记录很多关于内存的信息,方便开发时进行调试。
![](https://static001.geekbang.org/infoq/6b/6b6db5e3954a2894727c15802e5824c3.webp?x-oss-process=image%2Fresize%2Cp_80%2Fformat%2Cpng)
python 内存管理机制
python 内存管理机制大致被分为四层
操作系统提供的内存管理接口,比如 malloc 和 free 接口,由操作系统实现和管理
Python 对于第 0 层的一个简单包装,主要是为了统一不同操作系统的行为,以 PyMem_为前缀
python 创建对象时不仅仅需要申请一块内存空间,还需要管理对象的类型参数以及初始化对象的引用计数值。以 PyObj_为前缀。
最上层针对一些常用的字符串对象和整数对象,提供一个对象缓存的机制。
而在 Python 中,对象间的引用是通过引用计数来管理的。当一个对象被引用时,其引用计数会增加;当不再被引用时,其引用计数会减少。当引用计数为 0 时,该对象就会被垃圾回收器回收。
PyTuple_SetItem 源码分析
首先 python 中 元组应该是不可变的,因此只有当其引用计数为 1,也就是没有其他人调用这个元组时,我们才可以对其元素进行更改。 因此最开始先要确认是否是元组类型以及元祖的引用计数是否为 1,如果不满足要求,则会让 newitem 的引用计数减少 1。
注意:如果在此之前 newitem 的引用计数为 1 ,经过减少则会变成 0,那么这个时候就相当于此函数会“偷走”一个对 newitem 的引用。可能会导致该元素提前被释放. 这可能会造成内存风险,不过不是泄露。
接下来会进行索引有效性检查,风险同上,这里不过多赘述。
接下来会进行赋值,这个过程中,旧的元素的引用计数会减少(如果变成 0,也会提前被释放,不过这里是符合预期的),新的元素的计数会增加。
总结
也就是说 PyTuple_SetItem 存在两种可能,第一种:新元素引用计数减 1,第二种旧元素引用计数减 1,新元素引用计数加 1
因此我们必须要关注 PyTuple_SetItem 的操作是否会成功,如果成功,我们需要手动释放之前创建的 new_item(更准确的说,是减引用计数)。如果失败,我们就不能对 new_item 有任何操作。
作者:山花
链接:https://juejin.cn/post/7366972839780089906
来源:稀土掘金
评论