4

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 通にも満たないはずです。ここで何が問題なのですか?または、返されるメールの数を制限する方法はありますか? どうも!

4

1 に答える 1

0

問題を再現できなければ、原因が何であるかを確実に知る方法はありません (問題を引き起こす完全なプログラムを確認し、使用しているすべての依存関係のバージョンを知る必要があります)。

ただし、これが私の最善の推測です。Python のいくつかのバージョンには、非常にメモリを浪費する imaplib の実装が含まれています。この問題は Windows で特に顕著ですが、そのプラットフォームに限定されません。

問題の核心は、ソケットから読み取るときに文字列を割り当てる方法と、imaplib がソケットから文字列を読み取る方法です。

ソケットから読み取るとき、Python は最初に、アプリケーションが要求するバイト数を処理するのに十分な大きさのバッファーを割り当てます。これは妥当な音で、おそらく 16 kB です。次に、データがそのバッファーに読み込まれ、実際に読み取られたバイト数に合わせてバッファーのサイズが変更されます。

この操作の効率は、プラットフォームの再割り当ての実装の品質に依存します。バッファのサイズを変更すると、より適切な場所に移動することになり、サイズが小さいほど多くのメモリが浪費されなくなります。または、その領域の一部として割り当てられなくなったメモリの末尾部分を再利用可能としてマークするだけかもしれません (実際に再利用できる場合もあります)。または、技術的に割り当てられていないメモリが無駄になる可能性があります。

数十キロバイトのデータを読み取る必要があり、データがネットワークから一度に数十バイトずつ到着する場合、そのメモリが無駄になる累積的な影響を想像してみてください。さらに悪いことに、データが本当に細流で、一度に数バイトしか得られない場合を想像してみてください。または、数百 kB の非常に「大きな」応答を読んでいる場合。

浪費されるメモリの量 (プロセスによって効果的に割り当てられますが、意味のある方法で使用することはできません) は膨大になる可能性があります。100 kB のデータ、一度に 5 バイトを読み取るには、20480 バッファーが必要です。各バッファーが 16 kB から始まり、縮小に失敗して 16Kb のままになっている場合、その 100 kB のデータを保持するために少なくとも 320 MB のメモリを割り当てたことになります。

imaplib の一部のバージョンでは、バッファリングとコピーの複数のレイヤーを導入することで、この問題を悪化させていました。非常に古いバージョン (実際に使用していないことが望ましい) では、一度に 1 バイトを読み取ることさえあります (上記のシナリオでは、1.6GB のメモリ使用量になります)。

もちろん、この問題は通常、再アロケータがそれほど悪くない Linux では発生しません。そして、以前の Python リリース (最新の 2.x リリースより前) のさまざまな時点で、バグは「修正」されていたので、最近ではそれが現れるとは思わないでしょう。そして、これは、このように失敗する前に、プログラムがしばらくの間正常に実行された理由を説明していません。

しかし、それは私の最善の推測です。

于 2012-02-29T16:11:50.900 に答える