【发布时间】:2018-12-18 18:07:38
【问题描述】:
我正在观察我无法向自己解释的内存使用情况。下面我提供了我的实际代码的精简版本,它仍然表现出这种行为。该代码旨在完成以下任务:
以 1000 行为单位读取文本文件。每一行都是一个句子。将这 1000 个句子分成 4 个生成器。将这些生成器传递到线程池,并在 250 个句子上并行运行特征提取。
在我的实际代码中,我从整个文件的所有句子中积累了特征和标签。
现在奇怪的事情来了:即使没有累积这些值,内存也会被分配但不会再次释放!我认为它与线程池有关。总共占用的内存量取决于为任何给定单词提取了多少特征。我在这里用range(100) 模拟这个。看看:
from sys import argv
from itertools import chain, islice
from multiprocessing import Pool
from math import ceil
# dummyfied feature extraction function
# the lengt of the range determines howmuch mamory is used up in total,
# eventhough the objects are never stored
def features_from_sentence(sentence):
return [{'some feature' 'some value'} for i in range(100)], ['some label' for i in range(100)]
# split iterable into generator of generators of length `size`
def chunks(iterable, size=10):
iterator = iter(iterable)
for first in iterator:
yield chain([first], islice(iterator, size - 1))
def features_from_sentence_meta(l):
return list(map (features_from_sentence, l))
def make_X_and_Y_sets(sentences, i):
print(f'start: {i}')
pool = Pool()
# split sentences into a generator of 4 generators
sentence_chunks = chunks(sentences, ceil(50000/4))
# results is a list containing the lists of pairs of X and Y of all chunks
results = map(lambda x : x[0], pool.map(features_from_sentence_meta, sentence_chunks))
X, Y = zip(*results)
print(f'end: {i}')
return X, Y
# reads file in chunks of `lines_per_chunk` lines
def line_chunks(textfile, lines_per_chunk=1000):
chunk = []
i = 0
with open(textfile, 'r') as textfile:
for line in textfile:
if not line.split(): continue
i+=1
chunk.append(line.strip())
if i == lines_per_chunk:
yield chunk
i = 0
chunk = []
yield chunk
textfile = argv[1]
for i, line_chunk in enumerate(line_chunks(textfile)):
# stop processing file after 10 chunks to demonstrate
# that memory stays occupied (check your system monitor)
if i == 10:
while True:
pass
X_chunk, Y_chunk = make_X_and_Y_sets(line_chunk, i)
我用来调试的文件有 50000 行非空行,这就是我在一个地方使用硬编码的 50000 的原因。如果你想使用同一个文件,他是一个链接,方便你:
https://www.dropbox.com/s/v7nxb7vrrjim349/de_wiki_50000_lines?dl=0
现在,当您运行此脚本并打开系统监视器时,您会观察到内存已用完,并且使用量一直持续到第 10 个块,我人为地进入一个无限循环以证明内存仍在使用中,即使虽然我从不存储任何东西。
你能解释一下为什么会这样吗?我似乎遗漏了一些关于应该如何使用多处理池的内容。
【问题讨论】:
-
一旦你使用了一些内存,Python 很少将它返回给操作系统。这通常不是问题——内存没有泄漏,它只是在三个级别的空闲列表之一中,所以当你再次需要内存时,它不必允许,所以它运行得更快。当它是一个问题时,答案很简单:你已经在使用
multiprocessing,所以你可以经常回收池进程。 -
您的意思是我目前正在为每 1000 行块创建新池,并且在处理块后它们不会被销毁?如何为文件中的所有 1000 行块重用相同的 4 个线程?
-
好的,首先,您根本不是在创建线程,而是在创建进程。但是第二,不,在最初的 4 之后,您没有创建任何新流程。您正在一遍又一遍地重用相同的流程,就像您想要的那样。这正是内存没有被释放的原因:内存通常在进程退出之前不会被释放。但是,同样,这首先可能不是问题。
-
我的意思是流程,是的。但这里是个问题。那么我怎样才能按照您的建议回收(销毁并制造新的?)流程?
-
是什么让您认为这里有问题?您要解决的实际症状是什么?不是您测量的最无意义的值,而是您试图通过测量它们来诊断的症状?
标签: python memory-management memory-leaks multiprocessing threadpool