【问题标题】:Apparent memory leak with numpy tolist() in long running process在长时间运行的过程中使用 numpy tolist() 出现明显的内存泄漏
【发布时间】:2016-03-05 11:58:32
【问题描述】:

我运行一个服务器,它充当团队内客户的数据处理节点。最近,我们一直在重构服务器中的遗留代码,以利用 numpy 进行一些过滤/转换作业。

由于我们必须将此数据提供给远程客户端,因此我们将 numpy 数据转换为各种形式,使用 numpy.tolist() 作为中间步骤。

每个查询都是无状态的,没有全局变量,因此在查询之间不会维护任何引用。

在一个特定步骤中,我发现了明显的内存泄漏,我一直试图通过 memory_profiler 进行追踪。此步骤涉及将大型(>4m 条目)ndarray 浮点数转换为 python 列表。我第一次发出查询时,tolist() 调用分配了 120m 的内存,然后在我释放 numpy 数组时释放了 31m。第二次(以及随后的时间)我发出相同的查询,分配/解除分配是 31m。我发出的每个不同查询都有相同的模式,但绝对值不同。

为了便于说明,我已经拆散了我的代码,并强制输入了一些 del 命令。下面的输出来自 memory_profiler.profile

第一期查询:

Line #    Mem usage    Increment   Line Contents
================================================
   865    296.6 MiB      0.0 MiB           p = ikeyData[1]['value']
   866    417.2 MiB    120.6 MiB           newArr = p.tolist()
   867    417.2 MiB      0.0 MiB           del p
   868    385.6 MiB    -31.6 MiB           del ikeyData[1]['value']
   869    385.6 MiB      0.0 MiB           ikeyData[1]['value'] = newArr

同一查询的第二个(和后续)实例:

Line #    Mem usage    Increment   Line Contents
================================================
   865    494.7 MiB      0.0 MiB           p = ikeyData[1]['value']
   866    526.3 MiB     31.6 MiB           newArr = p.tolist()
   867    526.3 MiB      0.0 MiB           del p
   868    494.7 MiB    -31.6 MiB           del ikeyData[1]['value']
   869    494.7 MiB      0.0 MiB           ikeyData[1]['value'] = newArr

您可以想象,在一个具有高度可变查询的长期运行过程中,这些分配会不断累积,迫使我们定期反弹服务器。

有人对这里可能发生的事情有想法吗?

【问题讨论】:

  • 首先想到的是检查numpy的源码。
  • 如果只有第一次运行会产生过多的分配 - 它们是如何“建立”的?
  • @ivan_pozdeev 每个唯一查询都会产生不同的过度初始分配。我们在某一天有数千个独特的查询。
  • 正如linked questionMike's answer 所说,您应该检查Python 的GC 而不是原始内存占用。后者只有在前者什么也没显示的情况下才有用 - 然后发生过多的内存碎片,内存管理设施出现故障,或者numpy 正在泄漏(两者都极不可能)。没有minimal reproducible example,我们真的无话可说。

标签: python python-2.7 numpy


【解决方案1】:

在你的情况下,Python 可能已经释放了内存。

这并不意味着内存分配器一定会返回内存 到操作系统。 memory_profiler 使用系统调用来找出当前使用的内存量。所以你的代码可能没有问题。

【讨论】:

  • 如果他们“必须定期反弹服务器”,则可能有问题 - 无论是他们的代码还是其他问题。
  • 日志记录建议一次性使用未发布的 89 MiB。那里没有堆积的迹象。看起来问题出在其他地方。
  • @MikeMüller 每个唯一查询都会在该代码行产生一次分配。我是第一个承认问题可能出在其他地方的人,但这是我唯一能找到与记忆随时间增长相关的奇怪行为的地方。
  • 每个查询都是完全独立的,是的,它背后有线程。我考虑过线程架构是否保留了分配。但是,稍后在此代码中 ikeyData[1]['value'] 在我渲染到 JSON 时被丢弃。这发生在线程架构无法访问相关列表或 ikeyData 字典机制的层。从线程架构的角度来看,它调用了一个返回字符串的函数。
猜你喜欢
  • 1970-01-01
  • 2020-12-25
  • 2010-09-30
  • 1970-01-01
  • 2015-11-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-09
相关资源
最近更新 更多