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
Pythonはモジュールのアンロードをサポートしていません。
ただし、プログラムが時間の経過とともに無制限の数のモジュールをロードしない限り、それはメモリリークの原因ではありません。モジュールは通常、起動時に一度ロードされ、それだけです。メモリリークはおそらく他の場所にあります。
プログラムが時間の経過とともに無制限の数のモジュールを実際にロードするというまれなケースでは、おそらくプログラムを再設計する必要があります。 ;-)
Pythonについてはよくわかりませんが、他の言語ではgc.collect()
と同等の関数を呼び出すとnot未使用のメモリが解放されます-メモリが実際にある場合にのみ、そのメモリが解放されます必要。
それ以外の場合、モジュールを再度ロードする必要がある場合に備えて、モジュールを当面の間メモリに保持することは、Pythonにとって意味があります。
(もっと簡潔な質問を書くようにしてください。最初だけを読んで、残りをざっと読みました。)最初に簡単な問題を見つけました。
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つの問題があるため、実際にこれを行うのは少し難しいです。
これを一般的に使用するには、いくつかのトリッキーな問題があります。アンロードするモジュールに依存するモジュールを検出します。それらもアンロードしても大丈夫かどうかを知る(ユースケースに大きく依存します);これをすべて調べながらスレッドを処理する(imp.acquire_lockを見てください)、など。
これを行うと便利な場合もありますが、ほとんどの場合、コードが変更された場合にアプリを再起動することをお勧めします。おそらく頭痛の種になるでしょう。