Поиск в Python IMAP, результаты поиска исчерпывают всю память - PullRequest
4 голосов
/ 27 октября 2011

Я пытаюсь получить все письма с автоответом с определенного адреса в Python, используя imaplib. Все работало отлично в течение нескольких недель, но теперь каждый раз, когда я запускаю свою программу, вся моя оперативная память расходуется (несколько ГБ!), И сценарий в конечном итоге уничтожается убийцей 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 писем по несколько килобайт. Что может быть здесь? Или есть способ ограничить количество возвращаемых писем? Thx!

1 Ответ

0 голосов
/ 29 февраля 2012

Невозможно точно знать, в чем причина, не имея возможности воспроизвести проблему (и, конечно, не увидев полную программу, вызывающую проблему, и не зная версию всех зависимостей, которые вы используете).

Впрочем, вот мой лучший догадываюсь .Несколько версий Python включают очень бесполезную реализацию imaplib.Эта проблема особенно очевидна в Windows, но не ограничивается этой платформой.

Суть проблемы заключается в том, как строки распределяются при чтении из сокета, и как imaplib читает строки из сокетов.

При чтении из сокета Python сначала выделяет буфер, достаточно большой, чтобы обработать столько байтов, сколько требует приложение.Это может быть что-то разумное звучание, возможно, 16 кБ.Затем данные считываются в этот буфер, и размер буфера изменяется вниз , чтобы соответствовать количеству фактически прочитанных байтов.

Эффективность этой операции зависит от качества реализации перераспределения платформы,Изменение размера буфера может в конечном итоге переместить его в более подходящее место, где меньший размер позволяет избежать потери памяти.Или он может просто пометить хвостовую часть памяти, которая больше не выделяется как часть этой области, как пригодную для повторного использования (и он может даже использовать ее на практике).Или это может привести к потере этой технически нераспределенной памяти.

Представьте себе совокупный эффект от потери этой памяти, если вам нужно прочитать несколько десятков кБ данных, а данные поступают из сети несколькими десятками байтов ввремя.Хуже того, представьте, что данные действительно текут, и вы получаете только несколько байтов за раз.Или, если вы читаете очень «большой» ответ размером в несколько сотен килобайт.

Объем памяти, потраченный впустую - эффективно выделяемый процессом, но не используемый каким-либо значимым образом - может быть огромным.100 кБ данных, для чтения 5 байтов за раз требуется 20480 буферов.Если каждый буфер начинается с 16 кБ и безуспешно сокращается, в результате чего их остаются при 16 КБ, то вы выделили как минимум 320 МБ памяти для хранения этих 100 КБ данных.

Некоторые версии imaplib усугубили эту проблему, добавив несколько уровней буферизации и копирования.Очень старая версия (надеюсь, не та, которую вы на самом деле используете) даже читает по 1 байту за раз (что приведет к 1,6 ГБ использования памяти в приведенном выше сценарии).

Конечно, эта проблема обычно не появляется в Linux, где перераспределитель не так уж и плох.И в разные моменты в предыдущих выпусках Python (предшествующих самой последней версии 2.x) ошибка была «исправлена», поэтому я не ожидал увидеть ее в наши дни.И это не объясняет, почему ваша программа какое-то время работала нормально, прежде чем потерпела неудачу.

Но это мое лучшее предположение.

...