【问题标题】:Python IMAP search, search results exhaust all memoryPython IMAP搜索,搜索结果耗尽所有内存
【发布时间】:2011-12-16 08:34:47
【问题描述】:

我正在尝试使用 imaplib 从 Python 中的特定地址获取所有自动回复电子邮件。数周以来一切正常,但现在每次我运行我的程序时,我的所有 RAM 都会被消耗掉(几 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 封几千字节的电子邮件。这里可能是什么问题?或者有没有办法限制返回的电子邮件数量? 谢谢!

【问题讨论】:

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

标签: python search imaplib


【解决方案1】:

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

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

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

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

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

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

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

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

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

但这是我最好的猜测。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-02
    • 2019-02-24
    • 1970-01-01
    • 2011-10-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多