Python gc.collect()中float自由列表疑问及内存泄漏风险咨询
官方文档语句的真实含义
Not all items in some free lists may be freed due to the particular implementation, in particular float.
这句话核心意思是:Python内部的「对象自由列表」机制会缓存一部分已无引用的对象(尤其是float类型),即便调用gc.collect(),这部分缓存对象也不会被彻底释放回操作系统。
这不是说float对象完全无法被垃圾回收,而是Python为了优化性能,会把刚被销毁的float对象暂存在自由列表中,供后续创建新float时直接复用,避免频繁分配/释放内存的开销。
纠正你的误解:float并非完全不回收,int的情况有区别
你提到的[1.0,2.0,3.0]在失去引用后,这些float对象的引用计数会归零,但不会立刻释放给系统,而是被加入float的自由列表缓存;而[1,2,3]里的小整数(-5到256区间)是Python全局缓存的单例对象,不管有没有引用,它们本身就不会被销毁,这和垃圾回收机制无关——如果是超出该范围的大整数,失去引用后会被正常回收,不会进入类似float的自由列表。
直观示例:观测float自由列表的缓存行为
我们可以用tracemalloc观测内存变化,对比float和大整数的回收差异:
import gc import tracemalloc # 观测float的内存变化 tracemalloc.start() l_float = [float(i) for i in range(10000)] l_float = None gc.collect() current, peak = tracemalloc.get_traced_memory() print(f"float列表回收后,当前内存占用: {current/1024/1024:.2f} MB") tracemalloc.stop() # 观测大整数的内存变化 tracemalloc.start() l_int = [1000000 + i for i in range(10000)] l_int = None gc.collect() current, peak = tracemalloc.get_traced_memory() print(f"大整数列表回收后,当前内存占用: {current/1024/1024:.2f} MB") tracemalloc.stop()
运行后会发现,float列表回收后的内存占用略高于大整数情况——这就是部分float对象被保留在自由列表中,未释放给操作系统的证明。后续再创建新float对象时,内存不会明显上涨,因为复用了缓存的对象。
重复调用生成大量float的函数是否会内存泄漏?
不会出现内存泄漏。CPython中float的自由列表有固定大小限制(默认最多缓存100个空闲float对象,不同版本略有调整),当自由列表满了之后,新产生的空闲float对象会被正常释放给操作系统。
用你给出的代码测试(新增内存观测逻辑):
import random import psutil import os import gc def get_memory_usage(): process = psutil.Process(os.getpid()) return process.memory_info().rss / 1024 / 1024 # 单位MB def f(): l = [random.uniform(0, 1) for _ in range(100_000_000)] if __name__ == '__main__': count = 1 while count <= 5: print(f"{count=}, 当前内存: {get_memory_usage():.2f} MB") f() gc.collect() print(f"回收后内存: {get_memory_usage():.2f} MB") count += 1
运行后可见:第一次调用f()内存大幅上涨,回收后内存下降但不会回到初始值;第二次及之后调用f(),内存不再持续上涨,而是稳定在固定区间——这说明内存占用可控,不存在泄漏问题,只是自由列表缓存了少量float对象。
内容的提问来源于stack exchange,提问作者maplemaple

