Python IMAP搜索,结果占满所有内存

4 投票
1 回答
772 浏览
提问于 2025-04-17 05:05

我正在尝试使用 imaplib 在 Python 中从一个特定的邮箱地址获取所有自动回复邮件。之前一切都运行得很好,但现在每次运行我的程序时,内存(RAM)都会被消耗殆尽(好几GB!),最后脚本会被系统的内存管理程序杀掉。

这是我目前使用的代码:

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 封,每封邮件也就几千字节。这里可能是什么问题呢?或者有没有办法限制返回的邮件数量?谢谢!

1 个回答

0

要确切知道问题的原因,其实很难。我们需要能够重现这个问题,当然也需要看到完整的程序和你使用的所有依赖项的版本。

不过,我可以给你一个大概的猜测。几个版本的Python在处理imaplib时,内存使用得非常浪费。这个问题在Windows上特别明显,但不止于这个平台。

问题的核心在于从网络套接字读取字符串时,字符串是如何分配内存的,以及imaplib是如何从套接字读取字符串的。

当从套接字读取数据时,Python首先会分配一个足够大的缓冲区,以便处理应用程序请求的字节数。这个大小可能听起来合理,比如16KB。然后,数据会被读入这个缓冲区,并且缓冲区会被缩小到实际读取的字节数。

这个操作的效率取决于平台重新分配内存的实现质量。缩小缓冲区可能会把它移动到一个更合适的位置,这样小一点的大小就不会浪费太多内存。或者,它可能只是标记内存的尾部部分,不再作为该区域的一部分分配,标记为可重用(实际上可能也能重用)。也可能会导致那些技术上未分配的内存被浪费。

想象一下,如果你需要读取几十KB的数据,而这些数据是通过网络一点一点到达的,那浪费的内存会累积成多少。更糟糕的是,如果数据真的很少,每次只收到几字节,或者你在读取几百KB的“大”响应时。

浪费的内存量——实际上被进程分配了,但没有任何实际用途——可能会非常大。比如,读取100KB的数据,每次5字节,就需要20480个缓冲区。如果每个缓冲区开始时是16KB,但缩小失败,导致它们仍然保持在16KB,那么你就至少分配了320MB的内存来存放这100KB的数据。

某些版本的imaplib加剧了这个问题,引入了多个缓冲和复制的层次。一个非常老的版本(希望你没有在用)甚至每次只读取1字节(在这种情况下,内存使用量会达到1.6GB)。

当然,这个问题在Linux上通常不会出现,因为那里的重新分配器表现得还不错。而在之前的Python版本中(在最新的2.x版本之前),这个bug曾经被“修复”,所以我不认为现在会再出现这个问题。而且这也不能解释为什么你的程序之前运行得很好,后来才出现这种情况。

但这就是我最好的猜测。

撰写回答