在python中调用函数后,内存不会被释放

时间:2017-01-20 00:22:25

标签: python pandas memory garbage-collection

我一直在玩memory_profiler一段时间,并从下面的小程序中得到了这个有趣但令人困惑的结果:

import pandas as pd
import numpy as np

@profile
def f(p):
    tmp = []
    for _, frame in p.iteritems():
        tmp.append([list(record) for record in frame.to_records(index=False)])

# initialize a list of pandas panels
lp = []
for j in xrange(50):
    d = {}
    for i in xrange(50):
        df = pd.DataFrame(np.random.randn(200, 50))
        d[i] = df
    lp.append(pd.Panel(d))

# execution (iteration)
for panel in lp:
    f(panel)

然后,如果我使用memory_profiler的mprof来分析运行时的内存使用情况,mprof run test.py没有任何其他参数,我得到: memory_profiling_results_1.png

每个函数调用f()后似乎都没有释放内存。

tmp只是一个本地列表,每次调用f()时都应重新分配并重新分配内存。显然,附图中存在一些差异。我知道python有自己的内存管理块,并且还有int和其他类型的空闲列表,而gc.collect()应该有魔力。事实证明,明确的gc.collect()并不起作用。 (也许是因为我们正在使用熊猫对象,面板和框架?我不知道。)

最令人困惑的部分是,我不会更改或修改f()中的任何变量。它只是将一些列表表示副本放在本地列表中。因此python不需要复制任何东西。那么为什么以及如何发生这种情况呢?

=================

其他一些观察结果:

1)如果我用f()(最后一行代码)调用f(panel.copy()),传递副本而不是原始对象引用,我的内存使用结果完全不同:memory_profiling_results_2.png 。 python是否很聪明地告诉这个值传递的是一个副本,这样它可以做一些内部技巧来在每个函数调用后释放内存?

2)我认为这可能是因为df.to_records()。好吧,如果我将其更改为frame.values,我会得到类似的平坦记忆曲线,就像上面显示的memory_profiling_results_2.png一样,在迭代过程中(虽然我确实需要to_records()因为它保持了列dtype,而.values混淆了dtypes)。但我查看了to_records()上frame.py的实现。我不知道为什么它会占据那里的记忆,而.values可以正常工作。

我在Windows上运行程序,使用python 2.7.8,memory_profiler 0.43和psutil 5.0.1。

1 个答案:

答案 0 :(得分:0)

这不是内存泄漏。您所看到的是pandas.core.NDFrame缓存某些结果的副作用。这允许它在您第二次请求时返回相同的信息,而无需再次运行计算。将示例代码的结尾更改为以下代码并运行它。您应该发现第二次通过内存增加不会发生,并且执行时间将会减少。

import time

# execution (iteration)
start_time = time.time()
for panel in lp:
    f(panel)
print(time.time() - start_time)

print('-------------------------------------')
start_time = time.time()
for panel in lp:
    f(panel)
print(time.time() - start_time)