我用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而不使用任何其他参数,我会得到:
。在
每次函数调用f()后似乎都有未释放的内存。在
tmp只是一个本地列表,每次调用f()时都应重新分配和重新分配内存。很明显,在所附的图表中有一些差异。我知道python有它自己的内存管理块,也有int和其他类型的空闲列表,gc.collect()应该能做到这一点。结果是显式的gc.collect()不起作用。(也许是因为我们正在研究熊猫的物体、面板和框架?我不知道。)
最令人困惑的部分是,我不更改或修改f()中的任何变量。它所做的只是在本地列表中放置一些列表表示副本。因此python不需要复制任何东西。那么为什么会发生这种情况呢?在
==============
其他一些观察结果:
1)如果我用f(panel.copy())(最后一行代码)调用f(),传递的是副本而不是原始对象引用,则会得到完全不同的内存使用结果:
。python是否明智地告诉我们传递的值是一个副本,这样它就可以在每次函数调用后执行一些内部技巧来释放内存?在
2)我想可能是因为df.to_records()。如果我把它改成frame.values,我会得到类似的平坦内存曲线,就像上面所示的memory_profiling_results_2.png,在迭代过程中(虽然我确实需要to_records(),因为它维护列数据类型,而.values则会扰乱数据类型)。但我查过了帧.py的实现。我不明白为什么它会保留内存,而.values却可以正常工作。在
我在Windows上运行这个程序,使用python2.7.8、memory_profiler 0.43和psutil 5.0.1。在