Python无引用对象内存管理及gc.get_objects()引发内存泄漏的疑问
Python无引用对象内存管理及gc.get_objects()引发内存泄漏的疑问
我来帮你拆解这两个疑问,一步步说清楚~
一、无引用的A()对象,系统层面是怎么处理的?
你已经注意到,A()创建后没有被赋值给任何变量,Python的引用计数会立刻把它回收——这部分是Python解释器的逻辑,系统层面的处理细节是这样的:
- 当对象的引用计数降到0时,Python会先调用对象的
__del__方法(如果定义了的话),然后释放该对象占用的内存空间。 - 但Python自带的内存分配器(比如处理小对象的pymalloc)不会立刻把释放的内存还给操作系统,而是会把这部分内存缓存起来,留给后续新建的对象复用。这是为了减少频繁向OS申请/归还内存的系统调用开销,提升整体性能。
- 所以从系统进程的虚拟内存统计看,可能不会立刻看到内存下降,但实际上Python内部已经回收了这些无引用对象的内存,只是暂时缓存起来了,并没有真正的内存泄漏。
二、为什么gc.get_objects()会引发内存泄漏?
你的观察很准确——这个泄漏和gc.get_objects()的返回值持有引用直接相关,核心原因是形成了无法自动断开的引用链,具体拆解:
gc.get_objects()会返回一个包含所有当前被GC追踪对象的列表,而这个列表本身也是GC追踪的对象(因为它是可变容器类型)。- 当你第二次调用
gc.get_objects()时,返回的新列表里会包含上一次调用返回的旧列表(因为旧列表还没被回收,属于GC追踪对象)。 - 你把新列表赋值给
gc_objects变量后,旧列表的引用计数并没有降到0——因为新列表里持有它的引用,所以旧列表无法被GC回收。 - 以此类推,每次循环都会生成一个新列表,新列表引用旧列表,
gc_objects变量引用新列表,形成了一个无法断开的引用链,导致所有这些列表都一直占用内存,内存持续增长。
而你注释里提到的del gc_objects能解决问题,正是因为:
- 执行
del gc_objects后,当前的新列表的引用计数降到0,会被GC回收。 - 新列表被回收后,它引用的旧列表的引用计数也随之降到0,同样会被回收,整个引用链就被断开了,所有之前的列表都会被正确清理,内存也就不会泄漏了。
补充:你的代码输出验证
看你输出里每次新增的对象ID,其实就是每次调用gc.get_objects()返回的列表的ID——每次循环都会多一个这样的列表对象,而它们因为互相引用+被变量持有,无法被自动回收,直到你主动删除gc_objects变量。
比如你输出里的片段:
4965 [124362267579712, 124362267585728, 124362267337664]
后面的ID列表里,就包含了第一次调用gc.get_objects()返回的列表的ID,以及其他新增的GC追踪对象。
备注:内容来源于stack exchange,提问作者Daniel
相关产品推荐
相关产品推荐

