web-dev-qa-db-ja.com

Pythonでモジュールをアンロードします

TL/DR:

import gc, sys

print len(gc.get_objects()) # 4073 objects in memory

# Attempt to unload the module

import httplib
del sys.modules["httplib"]
httplib = None

gc.collect()
print len(gc.get_objects()) # 6745 objects in memory

更新 私はPythonこの問題について開発者に連絡しました、そして実際、それは モジュールをアンロードすることは不可能です 完全に「今後5年間で」(リンクを参照)

2.xでは、Pythonは、深刻で根本的で乗り越えられない技術的な問題のアンロードモジュールを実際にはサポートしていません。


アプリでmemleakを探している最近の調査で、モジュールに絞り込みました。つまり、アンロードされたモジュールをガベージコレクションできないことができません。以下に示すanyメソッドを使用してモジュールをアンロードすると、数千のオブジェクトがメモリに残ります。つまり、Pythonでモジュールをアンロードできません...

残りの問題は、どういうわけかモジュールのガベージコレクションを試みることです。

やってみよう:

import gc
import sys

sm = sys.modules.copy()  # httplib, which we'll try to unload isn't yet 
                         # in sys.modules, so, this isn't the source of problem

print len(gc.get_objects()) # 4074 objects in memory

sys.modulesのコピーを保存して、後で復元できるようにしましょう。したがって、これはベースライン4074オブジェクトです。理想的には、何とかしてこれに戻る必要があります。

モジュールをインポートしましょう:

import httplib
print len(gc.get_objects()) # 7063 objects in memory

最大7Kの非ガベージオブジェクトがあります。 sys.modulesからhttplibを削除してみましょう。

sys.modules.pop('httplib')
gc.collect()
print len(gc.get_objects()) # 7063 objects in memory

まあ、それはうまくいきませんでした。うーん、でも__main__に参照はありませんか?そうそう:

del httplib
gc.collect()
print len(gc.get_objects()) # 6746 objects in memory

やったー300オブジェクトそれでも、葉巻はありません。これは4000を超えるオリジナルのオブジェクトです。コピーからsys.modulesを復元してみましょう。

sys.modules = sm
gc.collect()
print len(gc.get_objects()) # 6746 objects in memory

うーん、まあ、それは無意味で、変化はありません。

globals().clear()
import gc # we need this since gc was in globals() too
gc.collect()
print len(gc.get_objects()) # 6746 objects in memory

地元の人?

locals().clear()
import gc # we need this since gc was in globals() too
gc.collect()
print len(gc.get_objects()) # 6746 objects in memory

何が.. imported内のモジュールをexecした場合はどうなりますか?

local_dict = {}
exec 'import httplib' in local_dict
del local_dict
gc.collect()
print len(gc.get_objects())  # back to 7063 objects in memory

さて、それは公平ではありません。それを__main__にインポートしました。なぜですか? local_dictを離れてはいけません...ああ!完全にインポートされたhttplibに戻ります。多分それをダミーオブジェクトに置き換えたら?

from types import ModuleType
import sys
print len(gc.get_objects())  # 7064 objects in memory

ブラッディ..... !!

sys.modules['httplib'] = ModuleType('httplib')
print len(gc.get_objects())  # 7066 objects in memory

モジュールを死になさい、死になさい!

import httplib
for attr in dir(httplib):
    setattr(httplib, attr, None)
gc.collect()
print len(gc.get_objects())  # 6749 objects in memory

さて、すべての試みの後、最善は開始点から+2675(ほぼ+ 50%)です...それはただ1つのモジュールからのものです...それは内部に大きなものさえもありません...

さて、今真剣に、私のエラーはどこですか?モジュールをアンロードして、その内容をすべて消去するにはどうすればよいですか?または、Pythonのモジュールは1つの巨大なメモリリークですか?

より簡単なコピー形式の完全なソース: http://Gist.github.com/450606

54
Slava V

Pythonはモジュールのアンロードをサポートしていません。

ただし、プログラムが時間の経過とともに無制限の数のモジュールをロードしない限り、それはメモリリークの原因ではありません。モジュールは通常、起動時に一度ロードされ、それだけです。メモリリークはおそらく他の場所にあります。

プログラムが時間の経過とともに無制限の数のモジュールを実際にロードするというまれなケースでは、おそらくプログラムを再設計する必要があります。 ;-)

16

Pythonについてはよくわかりませんが、他の言語ではgc.collect()と同等の関数を呼び出すとnot未使用のメモリが解放されます-メモリが実際にある場合にのみ、そのメモリが解放されます必要。

それ以外の場合、モジュールを再度ロードする必要がある場合に備えて、モジュールを当面の間メモリに保持することは、Pythonにとって意味があります。

Python's small object manager rarely returns memory back to the Operating System.ここ および ここ から。つまり、厳密に言えば、pythonには、オブジェクトが「gcで収集」されている場合でも、(設計上)一種のメモリリークがあります。

0
ilias iliadis

(もっと簡潔な質問を書くようにしてください。最初だけを読んで、残りをざっと読みました。)最初に簡単な問題を見つけました。

sm = sys.modules.copy()

Sys.modulesのコピーを作成したので、コピーにはモジュールへの参照が含まれているため、もちろん収集されません。 gc.get_referrersを使用して、それが何を参照しているかを確認できます。

これはうまくいきます:

# module1.py
class test(object):
    def __del__(self):
        print "unloaded module1"
a = test()

print "loaded module1"

# testing.py
def run():
    print "importing module1"
    import module1
    print "finished importing module1"

def main():
    run()
    import sys
    del sys.modules["module1"]
    print "finished"

if __== '__main__':
    main()

module1は、sys.modulesから削除するとすぐにアンロードされます。これは、モジュールへの参照が残っていないためです。 (module1 = Noneインポート後も機能します。わかりやすくするために、別の関数にインポートを配置しました。あなたがしなければならないすべてはそれへのあなたの参照を落とすことです。)

2つの問題があるため、実際にこれを行うのは少し難しいです。

  • モジュールを収集するには、そのモジュールへのすべての参照が到達できない必要があります(オブジェクトの収集と同様)。つまり、それをインポートした他のすべてのモジュールも逆参照して再ロードする必要があります。
  • モジュールが他の場所で参照されているときにsys.modulesからモジュールを削除すると、異常な状況が発生します。モジュールは引き続きロードされ、コードによって使用されますが、モジュールローダーはそれを認識しません。次回モジュールをインポートするときは、既存のモジュールへの参照が取得されないため(そのレコードを削除したため)、モジュールの2番目の共存コピーが読み込まれます。これは深刻な一貫性の問題を引き起こす可能性があります。したがって、最終的にsys.modulesから削除する前に、モジュールへの参照が残っていないことを確認してください。

これを一般的に使用するには、いくつかのトリッキーな問題があります。アンロードするモジュールに依存するモジュールを検出します。それらもアンロードしても大丈夫かどうかを知る(ユースケースに大きく依存します);これをすべて調べながらスレッドを処理する(imp.acquire_lockを見てください)、など。

これを行うと便利な場合もありますが、ほとんどの場合、コードが変更された場合にアプリを再起動することをお勧めします。おそらく頭痛の種になるでしょう。

0
Glenn Maynard