You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ubuntu 14.04下Python嵌套列表为何出现内存泄漏?

问题分析与解决方案

首先,你遇到的不是真正的内存泄漏,而是Python 2.7结合旧版Linux glibc(Ubuntu 14.04使用的glibc 2.19)的内存分配器行为导致的"内存保留"现象,属于典型的高水位线(high-water mark)问题。下面详细拆解原因和解决办法:

为什么会出现这种情况?

  1. Python 2的小对象内存池机制
    Python 2使用pymalloc作为小对象(小于256字节)的分配器,它会从操作系统一次性申请大块内存,再自行拆分给小对象使用。当你创建10^7个包含浮点数的小列表时,会触发大量小对象分配,直接导致内存池规模膨胀。
    当你执行del temp和gc.collect()后,Python确实释放了这些对象的内存,但pymalloc并不会立刻把所有空闲内存归还给操作系统——它会保留这些内存池,以便后续小对象分配时快速复用,避免频繁向OS申请内存的开销。

  2. 旧版glibc的内存回收策略
    Ubuntu 14.04的glibc 2.19使用的ptmalloc2分配器,在内存归还逻辑上比较保守:只有当内存池中的空闲块足够大且连续时,才会归还给操作系统。而嵌套列表[[0.1]]释放后,内存池里的空闲块往往比较零散,无法满足glibc的归还条件,导致内存被保留下来,表现为常驻内存居高不下。

  3. 不同环境的差异原因

    • OSX无此问题:OSX使用的libmalloc内存分配器在内存回收时更积极,会主动将空闲内存归还给系统,不会保留过多闲置内存。
    • Python3无此问题:Python3对pymalloc做了大量优化,调整了内存池的回收策略,空闲内存较多时会主动归还给操作系统;同时Python3的对象结构设计更高效,从根源减少了这类内存保留的情况。
    • [[] for ...]或[0.1 for ...]无问题:空列表的内存占用极小,释放后内存池的空闲块更容易合并成大块,触发glibc的归还逻辑;而纯浮点数列表是连续分配的单一类型对象,释放后内存块更规整,也更容易被归还给系统。

已知解决方案

  • 切换到Python3:这是最彻底的解决办法,Python3从根本上优化了内存管理机制,完全避免了这类问题。
  • 手动调用glibc的内存修剪函数:在Python2中,可以通过ctypes调用glibc的malloc_trim(0),强制让分配器把空闲内存归还给操作系统:
    import gc
    import ctypes
    
    temp = [[0.1] for _ in xrange(10 ** 7)]
    del temp
    gc.collect()
    # 强制修剪内存池,归还空闲内存给系统
    libc = ctypes.CDLL("libc.so.6")
    libc.malloc_trim(0)
    
    调用后你会看到常驻内存明显下降到接近初始值。
  • 使用更高效的数据结构:如果业务场景允许,改用numpy数组等紧凑的数据结构代替嵌套列表,不仅内存占用更低,还能避免触发小对象内存池的膨胀问题:
    import numpy as np
    temp = np.full((10**7, 1), 0.1)
    
  • 调整Python环境变量:可以尝试设置PYTHONMALLOC=malloc强制Python使用系统的malloc而非pymalloc,但这可能会降低小对象分配的性能,需要根据实际场景权衡。

内容的提问来源于stack exchange,提问作者Tim Martin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 04:24:35