11

かなりの量のメモリを消費する可能性のある非常に大きな Python クラスがあるとします。クラスには、インタープリターが終了したときにいくつかのものをクリーンアップする責任があるいくつかのメソッドがあり、atexit モジュールに登録されます。

import atexit
import os

class ReallyBigClass(object):
    def __init__(self, cache_file):
        self.cache_file = open(cache_file)
        self.data = <some large chunk of data>
        atexit.register(self.cleanup)

    <insert other methods for manipulating self.data>

    def cleanup(self):
        os.remove(self.cache_file)

このクラスのさまざまなインスタンスは、プログラムの存続期間中に出入りする可能性があります。私の質問は次のとおりです。

delたとえば、インスタンスへの他のすべての参照の場合、インスタンス メソッドを atexit に登録することは安全ですか? 言い換えれば、atexit.register()従来のバインディングと同じ方法で参照カウンターをインクリメントしますか? もしそうなら、メソッドの 1 つが atexit に登録されているため、クラス インスタンス全体がメモリ内でハングアップし、終了するまで待機する必要がありますか? または、インスタンスの一部をガベージ コレクションできますか? ガベージ コレクションが効果的に行われるように、このような一時的なクラス インスタンスの終了時にそのようなクリーンアップを構成するための好ましい方法は何でしょうか?

4

2 に答える 2

17

インスタンス メソッドを に登録するとatexit、インタープリターが終了するまでクラス インスタンス全体が保持されます。atexit解決策は、登録されているすべての関数をクラスから分離することです。その後、インスタンスを正常にガベージ コレクションできます。例えば、

import atexit
import os
import gc
import random

class BigClass1(object):
    """atexit function tied to instance method"""
    def __init__(self, cache_filename):
        self.cache_filename = cache_filename
        self.cache_file = open(cache_filename, 'wb')
        self.data = [random.random() for i in range(10000000)]
        atexit.register(self.cleanup)

    def cleanup(self):
        self.cache_file.close()
        os.remove(self.cache_filename)

class BigClass2(object):
    def __init__(self, cache_filename):
        """atexit function decoupled from instance"""
        self.cache_filename = cache_filename
        cache_file = open(cache_filename, 'wb')
        self.cache_file = cache_file
        self.data = [random.random() for i in range(10000000)]

        def cleanup():
            cache_file.close()
            os.remove(cache_filename)

        atexit.register(cleanup)

if __name__ == "__main__":
    import pdb; pdb.set_trace()

    big_data1 = BigClass1('cache_file1')
    del big_data1
    gc.collect()

    big_data2 = BigClass2('cache_file2')
    del big_data2
    gc.collect()

この行を 1 行ずつ調べてプロセス メモリを監視すると、 によって消費されたメモリは、の後にガベージ コレクションが成功しbig_data1ている間、インタプリタが終了するまで保持されることがわかります。各テスト ケースを単独で実行する (他のテスト ケースをコメント アウトする) と、同じ結果が得られます。big_data2del

于 2013-08-15T17:39:26.153 に答える
1

atexit実装のための私の想像は以下のようなものです:

try:
    # Your whole program
finally:
    if sys.exitfunc:
        sys.exitfunc()

モジュールatexitはsys.exitfuncを独自のコールバックに設定するだけです。そのため、インタープリターのシャットダウンによるクリーンアップによってオブジェクトが消失することを心配する必要はありません。

注1:sys.exitfuncが呼び出された場合に何が起こるかは指定されていませんsys.exit(EXIT_CODE)が、私の場合は「EXIT_CODE」が返されます

注2:atexit登録されたすべての関数を順番に実行し、すべての例外をキャッチするため、sys.exitの実行も非表示にします(SystemExit例外を発生させるだけなので)

于 2013-05-02T08:25:28.690 に答える