Python IMAP搜索,搜索结果耗尽所有内存

Python IMAP搜索,搜索结果耗尽所有内存,python,search,imaplib,Python,Search,Imaplib,我正在尝试使用imaplib从Python中的特定地址获取所有自动回复电子邮件。几周来一切正常,但现在每次我运行我的程序,我所有的内存都被消耗掉了(几GB!),脚本最终被OOM杀手杀死 以下是我当前使用的代码: M = imaplib.IMAP4_SSL('server') M.LOGIN('user', 'pass') M.SELECT() date = (datetime.date.today() - datetime.timedelta(1)).strftime("%d-%b-%Y") r

我正在尝试使用imaplib从Python中的特定地址获取所有自动回复电子邮件。几周来一切正常,但现在每次我运行我的程序,我所有的内存都被消耗掉了(几GB!),脚本最终被OOM杀手杀死

以下是我当前使用的代码:

M = imaplib.IMAP4_SSL('server')
M.LOGIN('user', 'pass')
M.SELECT()
date = (datetime.date.today() - datetime.timedelta(1)).strftime("%d-%b-%Y")
result, data = M.uid('search', None, '(SENTON %s HEADER FROM "auto@site.com" NOT SUBJECT "RE:")' % date)
...
我确信应该返回少于100封几千字节的电子邮件。这里可能是怎么回事?或者有没有办法限制返回的电子邮件数量?
谢谢

如果不能重现问题,就无法确定原因是什么(当然,如果没有看到触发问题的完整程序,并且知道所使用的所有依赖项的版本,也无法确定)

然而,这是我最好的猜测。Python的几个版本包括一个非常浪费内存的imaplib实现。这个问题在Windows上尤其明显,但不限于该平台

问题的核心是从套接字读取字符串时分配字符串的方式,以及imaplib从套接字读取字符串的方式

当从套接字读取数据时,Python首先分配一个足够大的缓冲区,以处理应用程序要求的字节数。这听起来可能合理,可能是16KB。然后将数据读入该缓冲区,并调整缓冲区的大小以适应实际读取的字节数

此操作的效率取决于平台重新分配实施的质量。调整缓冲区大小可能最终会将其移动到更合适的位置,在该位置,较小的缓冲区大小可以避免浪费大量内存。或者,它可能只是将不再作为该区域的一部分分配的内存的尾部标记为可重用(实际上它甚至可以重用)。或者它最终可能会浪费技术上未分配的内存

想象一下,如果您必须读取几十kB的数据,并且数据一次从网络到达几十字节,那么内存被浪费的累积效应。更糟糕的是,想象一下如果数据真的是涓涓细流,而您一次只能得到几个字节。或者,如果您正在读取几百kB的“大”响应

浪费的内存量——由进程有效分配,但不能以任何有意义的方式使用——可能是巨大的。100 kB的数据,一次读取5个字节需要20480个缓冲区。如果每个缓冲区开始时为16Kb,但收缩失败,导致它们保持16Kb,那么您至少分配了320MB的内存来保存100KB的数据

imaplib的某些版本引入了多层缓冲和复制,从而加剧了这一问题。一个非常旧的版本(希望不是您实际使用的版本)甚至一次读取1个字节(在上述场景中,这将导致1.6GB的内存使用)

当然,这个问题通常不会在Linux上出现,因为在Linux上重新分配程序并不是那么糟糕。在以前的Python版本中(在最近的2.x版本之前),这个bug已经“修复”了,所以我不希望这些天看到它出现。这并不能解释为什么你的程序在以这种方式失败之前运行了一段时间


但这是我最好的猜测。

(1)我认为您代码中的大写字母是一个打字错误,因为它不会按原样工作。(2) 你不会收到任何电子邮件,只会收到UID(因此邮件大小无关紧要)。(3) 尝试在搜索之前添加“M.debug=4”-这将显示发送到服务器和从服务器接收到的内容。要限制,您只能在消息的子集内搜索,这具有相同的效果(除了限制最大结果,而不是总结果)。感谢您的回复。我在M.select()之前添加了'M.debug=4',现在一切都像以前一样发生了。代码与IMAP搜索字符串相同。如果问题再次发生,我将在这里粘贴首次输出。