17

文字列を読み取るために Python で 3 GB のファイルを開いています。次に、このデータを辞書に保存します。私の次の目標は、この辞書を使用してグラフを作成することです。そのため、メモリ使用量を注意深く監視しています。

Python が 3 GB のファイル全体をメモリにロードしているように思えますが、それを取り除くことはできません。私のコードは次のようになります:

with open(filename) as data:

    accounts = dict()

    for line in data:
        username = line.split()[1]
        IP = line.split()[0]

        try:
            accounts[username].add(IP)
        except KeyError:
            accounts[username] = set()
            accounts[username].add(IP)

print "The accounts will be deleted from memory in 5 seconds"
time.sleep(5)
accounts.clear()

print "The accounts have been deleted from memory"
time.sleep(5)

print "End of script"

メモリ使用量を監視できるように、最後の行があります。このスクリプトは、3 GB を少し超えるメモリを使用します。辞書をクリアすると、約 300 MB が解放されます。スクリプトが終了すると、残りのメモリは解放されます。

私はUbuntuを使用しており、ターミナルで「システムモニター」と「無料」コマンドの両方を使用してメモリ使用量を監視しました。

私が理解していないのは、辞書をクリアした後、Python がなぜそんなに多くのメモリを必要とするのかということです。ファイルはまだメモリに保存されていますか? もしそうなら、どうすればそれを取り除くことができますか? 解放されたメモリが認識されないのは、OS に問題がありますか?

編集:辞書をクリアした後、 gc.collect() を強制しようとしましたが、役に立ちませんでした。

EDIT2:Ubuntu 12.04.LTSでPython 2.7.3を実行しています

EDIT3:非常に重要なことを言い忘れていたことに気づきました。私の本当の問題は、私の OS が Python によって使用されたメモリを「取り戻さない」ことではありません。後で、Python はそのメモリを再利用しないようです (OS により多くのメモリを要求するだけです)。

4

4 に答える 4

17

これは私にもまったく意味がありません。私はこれがどのように/なぜ起こるのかを理解したかったのです。(これもこのように機能するはずだと思いました!)ファイルを小さくして、自分のマシンに複製しました。

ここで2つの個別の問題が発生しました

  1. Pythonがファイルをメモリに読み取るのはなぜですか(怠惰な行の読み取りでは、そうではありません-正しいですか?)
  2. Pythonがシステムにメモリを解放しないのはなぜですか

私はPythonの内部についてまったく知識がないので、Web検索をたくさん行いました。これらはすべて、完全にマークから外れている可能性があります。(私はもうほとんど開発していません。過去数年間、テクノロジーのビジネス側にいます)

怠惰な行の読み取り...

私は周りを見回してこの投稿を見つけました-

http://www.peterbe.com/plog/blogitem-040312-1

それはずっと以前のバージョンのpythonからのものですが、この行は私に共鳴しました:

readlines()はファイル全体を一度に読み込み、行ごとに分割します。

それから私はこれも古い、effbotの投稿を見ました:

http://effbot.org/zone/readline-performance.htm

重要なポイントはこれでした:

たとえば、十分なメモリがある場合は、readlinesメソッドを使用して、ファイル全体をメモリに丸呑みすることができます。

この:

Python 2.2以降では、ファイルオブジェクト自体をループできます。これは、内部のreadlines(N)とほとんど同じように機能しますが、見た目ははるかに優れています。

xreadlinesのpythonsドキュメントを見る[ http://docs.python.org/library/stdtypes.html?highlight=readline#file.xreadlines ]:

このメソッドは、iter(f)と同じものを返します。バージョン2.3以降非推奨:代わりにファイル内の行に使用してください。

たぶん、なんらかの丸呑みが起こっているのではないかと思いました。

したがって、readlines[ http://docs.python.org/library/stdtypes.html?highlight=readline#file.readlines ]..を見ると

readline()を使用してEOFまで読み取り、読み取った行を含むリストを返します。

それがここで起こっていることのようです。

しかし、readlineは私たちが望んでいたもののように見えました[ http://docs.python.org/library/stdtypes.html?highlight=readline#file.readline ]

ファイルから1行全体を読み取ります

だから私はこれをreadlineに切り替えようとしましたが、プロセスは40MBを超えることはありませんでした(以前はログファイルのサイズである200MBに成長していました)

accounts = dict()
data= open(filename)
for line in data.readline():
    info = line.split("LOG:")
    if len(info) == 2 :
        ( a , b ) = info
        try:
            accounts[a].add(True)
        except KeyError:
            accounts[a] = set()
            accounts[a].add(True)

私の推測では、私たちは本当に怠惰ではありませんfor x in data-構成を使用してファイルを読んでいます-すべてのドキュメントとstackoverflowコメントは私たちが怠惰であることを示唆していますが。 readline()私にとってはかなり少ないメモリをrealdlines消費し、ほぼ同じ量のメモリを消費しましたfor line in data

メモリを解放する

メモリを解放するという点では、Pythonの内部についてはあまり詳しくありませんが、mod_perlで作業したときのことを思い出します... 500MBのファイルを開くと、そのapacheの子はそのサイズに成長しました。メモリを解放した場合、その子内でのみ解放されます。ガベージコレクションされたメモリは、プロセスが終了するまでOSに戻されませんでした。

だから私はそのアイデアをざっと見て、これが起こっているかもしれないことを示唆するいくつかのリンクを見つけました:

http://effbot.org/pyfaq/why-doesnt-python-release-the-memory-when-i-delete-a-large-object.htm

大きなオブジェクトを作成して再度削除すると、Pythonがメモリを解放した可能性がありますが、関連するメモリアロケータは必ずしもメモリをオペレーティングシステムに返すとは限らないため、Pythonプロセスがより多くの仮想メモリを使用しているように見える場合があります実際に使用するよりも。

それはちょっと古いもので、その後pythonにランダムな(受け入れられた)パッチがたくさん見つかりました。これは、動作が変更され、メモリをOSに戻すことができることを示唆しています(これらのパッチのほとんどが送信され、明らかに承認された2005年現在)。

それから私はこの投稿http://objectmix.com/python/17293-python-memory-handling.htmlを見つけました-そしてコメント#4に注意してください

"" "-パッチ#1123430:Pythonのスモールオブジェクトアロケータは、アリーナfree()内のすべてのメモリが再び使用されなくなったときに、アリーナをシステムに返すようになりました。Python2.5より前は、アリーナ(256KBのメモリチャンク)が解放されることはありませんでした。一部のアプリケーションでは、仮想メモリサイズの低下、特に、一時的に多数の小さなオブジェクトを使用する長時間実行アプリケーション。PythonがアリーナをプラットフォームCに返す場合free()、プラットフォームCライブラリが次に、そのメモリをオペレーティングシステムに戻します。パッチの効果は、それを不可能にすることをやめることであり、テストでは、少なくともMicrosoftCおよびgccベースのシステムで効果的であるように見えます。ハードワークと忍耐力を提供してくれたEvanJonesに感謝します。 。

したがって、Linuxで2.4を使用すると(テストしたとおり)、収集される多数の小さなオブジェクトに関して、使用済みのメモリが常に元に戻るとは限りません。

したがって、(私が思うに)f.read()とf.readlines()を実行することの違いは、前者はファイル全体を1つの大きな文字列オブジェクト(つまり、小さなオブジェクトではない)として読み取るのに対し、後者は各行がPythonオブジェクトである行のリスト。

'for line in data:'構文が本質的にラッピングreadlinesであり、そうでない場合readline、おそらくこれはそれと関係がありますか?おそらく、3GBのオブジェクトが1つあるという問題ではなく、数百万の30kのオブジェクトがあるという問題です。

于 2012-09-14T00:02:33.230 に答える
4

これを試しているpythonのバージョンはどれですか?

Python 2.7/Win7 でテストを行ったところ、期待どおりに動作し、メモリが解放されました。

ここで、あなたのようなサンプルデータを生成します:

import random

fn = random.randint

with open('ips.txt', 'w') as f: 
    for i in xrange(9000000):
        f.write('{0}.{1}.{2}.{3} username-{4}\n'.format(
            fn(0,255),
            fn(0,255),
            fn(0,255),
            fn(0,255),
            fn(0, 9000000),
        ))

そして、あなたのスクリプト。例外をスローするとコードが遅くなるため、dictdefaultdictに置き換えました。

import time
from collections import defaultdict

def read_file(filename):
    with open(filename) as data:

        accounts = defaultdict(set)

        for line in data:
            IP, username = line.split()[:2]
            accounts[username].add(IP)

    print "The accounts will be deleted from memory in 5 seconds"
    time.sleep(5)
    accounts.clear()

    print "The accounts have been deleted from memory"
    time.sleep(5)

    print "End of script"

if __name__ == '__main__':
    read_file('ips.txt')

ご覧のとおり、メモリは 1.4G に達してから解放され、36MB が残りました。

defaultdict でのメモリ使用量

元のスクリプトを使用すると、同じ結果が得られましたが、少し遅くなりました。

ここに画像の説明を入力

于 2012-09-14T02:51:03.927 に答える
1

Python がメモリを解放して Pythonで再利用する場合と、メモリを解放して OS に戻す場合には違いがあります。Python にはいくつかの種類のオブジェクト用の内部プールがあり、これらを再利用しますが、OS には返しません。

于 2012-09-14T02:04:46.227 に答える
0

gc モジュール、特にcollect関数が役立つ場合があります。私は自分で使ったことはありませんが、ドキュメントから、それは役に立ちそうです。あなたが走るgc.collect()前に私は走ってみますaccounts.clear()

于 2012-09-13T22:43:29.563 に答える