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

如何修复numpy.genfromtxt()引发的内存泄漏问题?

内存泄漏问题分析与解决方案

问题背景

程序通过numpy.genfromtxt()持续读取CSV文件生成浮点数组,运行数天后内存占用飙升至数GB。垃圾回收检测到LineSplitter对象及其绑定方法无法被回收,且随程序运行不断累积。

根因分析

numpy.genfromtxt内部依赖LineSplitter类处理CSV行分割,该类实例存在引用循环或内部缓存机制,导致垃圾回收无法清理。加上进程池worker长期存活,未回收的对象持续占用内存,最终引发内存泄漏。

解决方案

方案1:替换genfromtxt为loadtxt

numpy.loadtxt是轻量型CSV读取函数,功能满足你的场景(读取浮点数组、跳过首尾行、指定列),且内部实现不会产生LineSplitter相关泄漏问题。

修改Data_Collection方法:

def Data_Collection(self):
    # 先获取文件总行数,实现skip_footer效果
    with open(self.file_to_read, 'r') as f:
        total_lines = sum(1 for _ in f)
    read_rows = total_lines - 2 - 2  # 减去skip_header和skip_footer的行数

    raw_ar = np.loadtxt(
        self.file_to_read,
        delimiter=",",
        skiprows=2,
        max_rows=read_rows,
        usecols=self.arguments,
        dtype=np.float64,
        fill_values=1
    ).T

    # 直接取值避免不必要拷贝
    ar1 = (float(raw_ar[0][0]), float(raw_ar[0][-1]))
    
    d_df = np.delete(arr=raw_ar, obj=0, axis=0)
    
    # 用numpy切片高效拆分数组
    ddf = d_df[::2, :]
    dtf = d_df[1::2, :]
    
    tpl = (ddf, dtf)
    
    return (ar1, tpl)

方案2:优化genfromtxt调用并强制垃圾回收

若必须保留genfromtxt,可在每次调用后显式触发垃圾回收,同时减少不必要的内存拷贝:

import gc

def Data_Collection(self):
    raw_ar = np.genfromtxt(
        self.file_to_read,
        delimiter=",",
        skip_header=2,
        skip_footer=2,
        usecols=self.arguments,
        loose=True,
        dtype=np.float64,
        filling_values=1
    ).T

    # 直接访问原数组元素,避免copy
    ar1 = (float(raw_ar[0][0]), float(raw_ar[0][-1]))
    
    d_df = np.delete(arr=raw_ar, obj=0, axis=0)
    
    # 切片替代列表推导式+append,减少内存开销
    ddf = d_df[::2, :]
    dtf = d_df[1::2, :]
    
    tpl = (ddf, dtf)
    
    # 显式删除引用并触发垃圾回收
    del raw_ar, d_df
    gc.collect()
    
    return (ar1, tpl)

方案3:定期重启进程池

进程池worker长期存活会累积内存垃圾,可定期重启进程池清理:

@classmethod
async def StandingBy(cls):
    global pooller
    task_count = 0
    MAX_TASKS_PER_WORKER = 100  # 每执行100次任务重启一次进程池
    
    while True:
        try:
            heart = AAA.Spine(cls.arguments)
            childp = await asyncio.wrap_future(pooller.submit(heart.Rendering, True))
            del childp
            del heart
            
            task_count += 1
            if task_count >= MAX_TASKS_PER_WORKER:
                pooller.shutdown()
                pooller = confu.ProcessPoolExecutor(2)
                task_count = 0

        except OSError as err:
            # 错误处理逻辑
        await asyncio.sleep(some)

额外优化建议

  • 避免使用列表推导式+append拆分数组,改用numpy切片操作更高效。
  • 移除不必要的raw_ar.copy()调用,直接访问原数组元素减少内存拷贝。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 13:45:20