首页 > 解决方案 > Pandas DataFrame内存重新分配不会释放旧内存,很快就会导致OOM错误

问题描述

我的理解是float64应该占用两倍的内存float32。我使用的是 pandas DataFrame,当我向其中添加 float32 列时,内存消耗非常低。但是,当我向其中添加float64列时,内存消耗的行为方式使我无法绕开我的脑袋

我正在使用以下代码来研究这两种行为:

为了float32

import numpy as np
import pandas as pd

narr = np.random.rand(500,1000000)

df = pd.DataFrame(narr)

for c, i in enumerate(df.columns):
    print(c,end='\r')
    df[str(i)+'dummy'] = (df[i].values/df[0].values).astype(np.float32)

以下是我每 100 次迭代记录的内存消耗(以 GB 为单位):

现在,对于 float64

代码:

import numpy as np
import pandas as pd

narr = np.random.rand(500,1000000)

df = pd.DataFrame(narr)

for c, i in enumerate(df.columns):
    print(c,end='\r')
    df[str(i)+'dummy'] = (df[i].values/df[0].values).astype(np.float64)

这里的行为很奇怪。每 100 次迭代:

  1. 迭代暂停了一段时间
  2. 内存消耗大约增加 5GB
  3. 内存消耗下降 1-4 GB
  4. 迭代继续,并且内存消耗在接下来的 100 次迭代中保持不变(稳定)
  5. 该过程从步骤 1 开始重复

请帮助我理解:

  1. 为什么内存消耗不是简单的float64= float32*2 关系?
  2. 为什么奇怪的内存增量模式float64?它似乎每 100 次迭代就会增加一次,它也首先显着上升,然后略微下降,并在接下来的 100 次迭代中保持在那里

编辑:我按照@hpaulj 的评论中的建议使用了 df._mgr。它揭示了:

  1. pandas 将所有相似的 dtyped 列组合在一个内存块中
  2. 添加到 DataFrame 的每个新列都作为单独的内存块添加
  3. pandas 可以拥有的最大内存块数是 100
  4. 因此,每 100 次迭代 pandas 会打乱数据集并重新分配内存,并在此过程中将相似 dtyped 块的内存块合并在一起
  5. 所以,问题实际上是,如果创建的初始数据集是dtype 的,floatx那么添加floatxdtyped 的新列将导致内存消耗爆炸。之所以会发生这种情况,是因为 pandas 会将整个 old_columns+new_columns 重新分配一个新的连续内存块并腾出较早的综片内存位置

我无法理解为什么会这样。有人会认为,一旦整个 DataFrame 被重新分配,那么 DataFrame 占用的旧内存位置就会被释放,但显然情况并非如此。

这是典型的内存泄漏案例吗?如果 Pandas 不负责释放旧内存,那就太奇怪了。

另外,我仍然不明白为什么在每 100 次迭代重新分配时,内存消耗会比它最终稳定下来的还要多。似乎熊猫确实释放了一些内存,但不是全部?

欢迎所有见解。

标签: python-3.xpandasdataframememorymemory-management

解决方案


推荐阅读