我为什么会在这个python循环中泄漏内存?

13 投票
2 回答
2954 浏览
提问于 2025-04-15 18:46

我正在写一个自定义的文件系统爬虫,这个爬虫需要处理通过sys.stdin传入的数百万个文件路径模式(globs)。我发现当运行这个脚本时,它的内存使用量随着时间的推移大幅增加,整个程序几乎变得非常缓慢。我下面写了一个简单的例子来展示这个问题。我是在做错什么,还是发现了Python或glob模块的一个bug?(我使用的是Python 2.5.2)


#!/usr/bin/env python
import glob
import sys
import gc

previous_num_objects = 0

for count, line in enumerate(sys.stdin):
   glob_result = glob.glob(line.rstrip('\n'))
   current_num_objects = len(gc.get_objects())
   new_objects = current_num_objects - previous_num_objects

   print "(%d) This: %d, New: %d, Garbage: %d, Collection Counts: %s"\
 % (count, current_num_objects, new_objects, len(gc.garbage), gc.get_count())
   previous_num_objects = current_num_objects

输出结果看起来是这样的:

(0) This: 4042, New: 4042, Python Garbage: 0, Python Collection Counts: (660, 5, 0)
(1) This: 4061, New: 19, Python Garbage: 0, Python Collection Counts: (90, 6, 0)
(2) This: 4064, New: 3, Python Garbage: 0, Python Collection Counts: (127, 6, 0)
(3) This: 4067, New: 3, Python Garbage: 0, Python Collection Counts: (130, 6, 0)
(4) This: 4070, New: 3, Python Garbage: 0, Python Collection Counts: (133, 6, 0)
(5) This: 4073, New: 3, Python Garbage: 0, Python Collection Counts: (136, 6, 0)
(6) This: 4076, New: 3, Python Garbage: 0, Python Collection Counts: (139, 6, 0)
(7) This: 4079, New: 3, Python Garbage: 0, Python Collection Counts: (142, 6, 0)
(8) This: 4082, New: 3, Python Garbage: 0, Python Collection Counts: (145, 6, 0)
(9) This: 4085, New: 3, Python Garbage: 0, Python Collection Counts: (148, 6, 0)

每进行100次迭代,就会释放100个对象,所以len(gc.get_objects()每100次迭代就增加200。len(gc.garbage)始终保持为0。第二代的垃圾回收次数缓慢增加,而第0代和第1代的次数则是上下波动。

2 个回答

2

我在我的系统上没有发现任何实际的内存泄漏,但我觉得你提到的“每100次迭代释放100个对象”可能是因为你在使用编译好的正则表达式缓存(通过glob模块)。如果你查看一下re.py文件,你会发现_MAXCACHE的默认值是100,而一旦达到这个值,整个缓存就会被清空(在_compile中)。如果你在调用gc之前先调用re.purge(),你可能会发现这个效果消失了。

(我在这里建议使用re.purge()只是为了检查缓存是否影响了你的垃圾回收结果。实际上,你的代码中不需要包含这个。)

不过,我怀疑这能解决你内存激增的问题。

7

我发现这个问题是和fnmatch模块有关的。glob.glob这个函数会调用fnmatch来进行文件匹配,而fnmatch有一个缓存,用来存储正则表达式,这个缓存是不会被清空的。所以在这种情况下,缓存会不断增长,而且没有限制。我已经对fnmatch库提交了一个bug报告。

[1]: http://bugs.python.org/issue7846 Python Bug

撰写回答