我pandas.DataFrame
在多线程代码中使用(实际上是DataFrame
被调用的自定义子类Sound
).我注意到我有内存泄漏,因为我的程序的内存使用量逐渐增加超过1000万,最终达到我的计算机内存的约100%并崩溃.
我使用objgraph来尝试跟踪这个泄漏,并发现实例的MyDataFrame
数量一直在增加而它不应该:它的run
方法中的每个线程都创建一个实例,进行一些计算,将结果保存在文件中退出...所以不应该保留任何参考.
使用objgraph
我发现内存中的所有数据帧都有一个类似的参考图:
我不知道这是否正常......看起来这就是将我的物品留在记忆中的原因.任何想法,建议,见解?
我有一个Python实用程序,它遍历tar.xz
文件并处理每个单独的文件.这是一个15MB的压缩文件,包含740MB的未压缩数据.
在内存非常有限的一台特定服务器上,程序因内存不足而崩溃.我使用objgraph来查看创建了哪些对象.事实证明,TarInfo
实例没有被释放.主循环类似于:
with tarfile.open(...) as tar:
while True:
next = tar.next()
stream = tar.extractfile(next)
process_stream()
iter+=1
if not iter%1000:
objgraph.show_growth(limit=10)
Run Code Online (Sandbox Code Playgroud)
输出非常一致:
TarInfo 2040 +1000
TarInfo 3040 +1000
TarInfo 4040 +1000
TarInfo 5040 +1000
TarInfo 6040 +1000
TarInfo 7040 +1000
TarInfo 8040 +1000
TarInfo 9040 +1000
TarInfo 10040 +1000
TarInfo 11040 +1000
TarInfo 12040 +1000
Run Code Online (Sandbox Code Playgroud)
这一直持续到所有30,000个文件都被处理完毕.
为了确保,我已经注释掉了创建流并处理流的行.内存使用情况保持不变 - TarInfo实例泄露.
我正在使用Python 3.4.1,这种行为在Ubuntu,OS X和Windows上是一致的.
我关注这个文档: https: //mg.pov.lt/objgraph/
\n\nobjgraph_test.py
:
import objgraph\nimport os\n\nx = [\'a\', \'1\', [2, 3]]\nfilename = os.path.dirname(__file__) + \'/objgraph_test.png\'\nobjgraph.show_refs([x], filename=filename)\n
Run Code Online (Sandbox Code Playgroud)\n\n当我尝试输出.png
图像文件时,它会抛出错误:
(venv) \xe2\x98\x81 python-codelab [master] \xe2\x9a\xa1 python3 /Users/ldu020/workspace/github.com/mrdulin/python-codelab/src/performance-optimization/memory-profile-and-objgraph/objgraph_test.py\nGraph written to /var/folders/38/s8g_rsm13yxd26nwyqzdp2shd351xb/T/objgraph-4hy982i9.dot (6 nodes)\nImage renderer (dot) not found, not doing anything else\n
Run Code Online (Sandbox Code Playgroud)\n\n我已经安装了xdot包。
\n\n(venv) \xe2\x98\x81 python-codelab [master] \xe2\x9a\xa1 pip3 list | grep -e \'xdot\\|objgraph\'\nobjgraph 3.4.1 \nxdot 1.1 \n
Run Code Online (Sandbox Code Playgroud)\n\n我该如何解决这个问题?
\n我试图在使用C/Cython扩展和Python的Python/NumPy程序中找到令人讨厌的内存泄漏的起源multiprocessing
.
每个子进程处理一个图像列表,并且每个子进程将输出数组(通常大约200-300MB)通过a Queue
发送到主进程.相当标准的地图/减少设置.
正如你可以想象的那样,内存泄漏可能会占据巨大的数量,并且当他们只需要5-6GB就会让多个进程快乐地超过20GB内存......这很烦人.
我已经尝试通过Valgrind运行Python的调试版本,并对内存泄漏进行四重检查,但没有发现任何内容.
我检查了我的Python代码是否悬挂了对我的数组的引用,还使用了NumPy的分配跟踪器来检查我的数组是否确实已经发布.他们是.
我做的最后一件事就是将GDB附加到我的一个进程上(这个坏男孩现在正以27GB RAM运行并计数)并将大部分堆转储到磁盘上.令我惊讶的是,转储的文件充满了零!大约7G的零值.
这是Python/NumPy中的标准内存分配行为吗?我是否遗漏了一些明显的东西,可以解释为什么没有这么多的记忆?如何正确管理内存?
编辑:为了记录,我正在运行NumPy 1.7.1和Python 2.7.3.
编辑2:我一直在监视进程strace
,似乎它一直在增加每个进程的断点(使用brk()
系统调用).
CPython实际上是否正确释放内存?那么C扩展,NumPy数组呢?谁决定何时调用brk()
,是Python本身还是底层库(libc
,......)?
下面是一个带有注释的strace日志示例,来自一次迭代(即一个输入图像集).请注意,断点不断增加,但我确保(在objgraph
)Python解释器中没有保留有意义的NumPy数组.
# Reading .inf files with metadata
# Pretty small, no brk()
open("1_tif_all/AIR00642_1.inf", O_RDONLY) = 6
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f9387fff000
munmap(0x7f9387fff000, 4096) = 0
open("1_tif_all/AIR00642_2.inf", O_RDONLY) = 6
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f9387fff000
munmap(0x7f9387fff000, 4096) = 0
open("1_tif_all/AIR00642_3.inf", O_RDONLY) = 6
mmap(NULL, …
Run Code Online (Sandbox Code Playgroud) 看代码:
import objgraph
import numpy as np
objgraph.show_growth()
j = 20
y = []
for i in range(5):
for l in range(j):
y.append(np.array([np.random.randint(500),np.random.randint(500)]))
print 'i:',i
objgraph.show_growth()
print '___'
#objgraph.show_most_common_types(limit=100)
j += 1
Run Code Online (Sandbox Code Playgroud)
结果是:
i: 1
wrapper_descriptor 1596 +3
weakref 625 +1
dict 870 +1
method_descriptor 824 +1
i: 2
i: 3
i: 4
Run Code Online (Sandbox Code Playgroud)
对于2,3和4时代,它没有显示任何增长.但它应该表明numpy.array的数量增长
我最近阅读了objgraph文档,我对以下代码感到困惑
>>> class MyBigFatObject(object):
... pass
...
>>> def computate_something(_cache={}):
... _cache[42] = dict(foo=MyBigFatObject(),
... bar=MyBigFatObject())
... # a very explicit and easy-to-find "leak" but oh well
... x = MyBigFatObject() # this one doesn't leak
Run Code Online (Sandbox Code Playgroud)
它表明"非常明显且容易找到'泄漏'".这有内存泄漏吗?这是dict _cache吗?