Pythonのメモリを手動で解放する方法
前のブログでは、1つのスクリプトの複数回の最適化について説明しました.当時は最適化が悪くないと思っていましたが、テストスタッフがテストしたとき、Pythonの大きな穴を踏んだことに気づきました.
上記の最適化では、500人のユーザーごとにいくつかの計算が行われ、ディスクファイルに結果が記録されます.メモリを消費し続けることなく、ディスクファイルに格納されると思っていました.しかし、実際には、Pythonの大きな穴はPythonがこれらのメモリを自動的にクリーンアップしないことです.これはそれ自体が実現することによって決まる.具体的な原因はネット上で多くの文章で紹介されていますが、ここではcopyではありません.
このブログでは、Pythonがメモリを解放しない現象が確かに存在することを説明するために、筆者の実験スクリプトを貼り付け、delを先に、gcを明示的に呼び出すソリューションを提案します.collect().
スクリプトと具体的な効果を参照してください.
実験環境一:Win 7,Python 2.7
実行結果は次のとおりです.
way 1とway 2の場合、結果はまったく同じで、プログラムメモリ消費ピークは326772 KBで、sleep 20秒でメモリリアルタイム消費は244820 KBであった.
way 3の場合、プログラムメモリ消費ピークは同じですが、sleepの場合、メモリのリアルタイム消費は6336 KBしかありません.
実験環境2:Ubuntu 14.10,Python 2.7.3
実行結果:
結論:
以上の説明では、delを呼び出すと、Pythonは本当にreleaseメモリではなく、メモリプールに配置し続けます.gcを明示的に呼び出すのみである.collect()の場合、本当にreleaseメモリが表示されます.
さらに、
実は前のブログのスクリプトに戻って、gcを導入させました.collect()を作成し、監視スクリプトを書いてメモリ消費状況を監視します.
その結果、メモリは500人のユーザーのグループごとに実行された後に回復するのではなく、約70 MBしか残っていないまで消費され続け、gcが機能しているように見えます.本環境では,機器はCloud instance,総メモリ2 G,利用可能メモリは約1 G,本スクリプトメモリの常用消費量は900 M-1 Gである.すなわち,このスクリプトではgcが直ちに機能するのではなく,システム利用可能なメモリが1−1.2 Gから70 M程度に低下したときにgcが機能し始める.この点は確かにおかしいが、このスクリプトがThreadで使われているgcとは知らなかった.collect()が関係しているかどうか、あるいはgcが機能しているかどうかはもともと制御可能ではない.筆者はまだ関連実験をしていないので、次のブログで検討を続けるかもしれません.
しかし、gcを使用しない場合、collect()では、元のスクリプトがシステムメモリを消費して殺されます.この点はsyslogから明らかである.
上記の最適化では、500人のユーザーごとにいくつかの計算が行われ、ディスクファイルに結果が記録されます.メモリを消費し続けることなく、ディスクファイルに格納されると思っていました.しかし、実際には、Pythonの大きな穴はPythonがこれらのメモリを自動的にクリーンアップしないことです.これはそれ自体が実現することによって決まる.具体的な原因はネット上で多くの文章で紹介されていますが、ここではcopyではありません.
このブログでは、Pythonがメモリを解放しない現象が確かに存在することを説明するために、筆者の実験スクリプトを貼り付け、delを先に、gcを明示的に呼び出すソリューションを提案します.collect().
スクリプトと具体的な効果を参照してください.
実験環境一:Win 7,Python 2.7
from time import sleep, time
import gc
def mem(way=1):
print time()
for i in range(10000000):
if way == 1:
pass
else: # way 2, 3
del i
print time()
if way == 1 or way == 2:
pass
else: # way 3
gc.collect()
print time()
if __name__ == "__main__":
print "Test way 1: just pass"
mem(way=1)
sleep(20)
print "Test way 2: just del"
mem(way=2)
sleep(20)
print "Test way 3: del, and then gc.collect()"
mem(way=3)
sleep(20)
実行結果は次のとおりです.
Test way 1: just pass
1426688589.47
1426688590.25
1426688590.25
Test way 2: just del
1426688610.25
1426688611.05
1426688611.05
Test way 3: del, and then gc.collect()
1426688631.05
1426688631.85
1426688631.95
way 1とway 2の場合、結果はまったく同じで、プログラムメモリ消費ピークは326772 KBで、sleep 20秒でメモリリアルタイム消費は244820 KBであった.
way 3の場合、プログラムメモリ消費ピークは同じですが、sleepの場合、メモリのリアルタイム消費は6336 KBしかありません.
実験環境2:Ubuntu 14.10,Python 2.7.3
実行結果:
Test way 1: just pass
1426689577.46
1426689579.41
1426689579.41
Test way 2: just del
1426689599.43
1426689601.1
1426689601.1
Test way 3: del, and then gc.collect()
1426689621.12
1426689622.8
1426689623.11
ubuntu@my_machine:~$ ps -aux | grep test_mem
Warning: bad ps syntax, perhaps a bogus '-'? See http://procps.sf.net/faq.html
ubuntu 9122 10.0 6.0 270916 245564 pts/1 S+ 14:39 0:03 python test_mem.py
ubuntu 9134 0.0 0.0 8104 924 pts/2 S+ 14:40 0:00 grep --color=auto test_mem
ubuntu@my_machine:~$ ps -aux | grep test_mem
Warning: bad ps syntax, perhaps a bogus '-'? See http://procps.sf.net/faq.html
ubuntu 9122 10.0 6.0 270916 245564 pts/1 S+ 14:39 0:03 python test_mem.py
ubuntu 9134 0.0 0.0 8104 924 pts/2 S+ 14:40 0:00 grep --color=auto test_mem
ubuntu@my_machine:~$ ps -aux | grep test_mem
Warning: bad ps syntax, perhaps a bogus '-'? See http://procps.sf.net/faq.html
ubuntu 9122 11.6 0.1 30956 5608 pts/1 S+ 14:39 0:05 python test_mem.py
結論:
以上の説明では、delを呼び出すと、Pythonは本当にreleaseメモリではなく、メモリプールに配置し続けます.gcを明示的に呼び出すのみである.collect()の場合、本当にreleaseメモリが表示されます.
さらに、
実は前のブログのスクリプトに戻って、gcを導入させました.collect()を作成し、監視スクリプトを書いてメモリ消費状況を監視します.
while ((1)); do ps -aux | sort -n -k5,6 | grep my_script; free; sleep 5; done
その結果、メモリは500人のユーザーのグループごとに実行された後に回復するのではなく、約70 MBしか残っていないまで消費され続け、gcが機能しているように見えます.本環境では,機器はCloud instance,総メモリ2 G,利用可能メモリは約1 G,本スクリプトメモリの常用消費量は900 M-1 Gである.すなわち,このスクリプトではgcが直ちに機能するのではなく,システム利用可能なメモリが1−1.2 Gから70 M程度に低下したときにgcが機能し始める.この点は確かにおかしいが、このスクリプトがThreadで使われているgcとは知らなかった.collect()が関係しているかどうか、あるいはgcが機能しているかどうかはもともと制御可能ではない.筆者はまだ関連実験をしていないので、次のブログで検討を続けるかもしれません.
しかし、gcを使用しない場合、collect()では、元のスクリプトがシステムメモリを消費して殺されます.この点はsyslogから明らかである.