前回のsnmpによるシステム監視でメモリとスワップメモリを使い果たしてハングアップすることがわかりました。思い当たることは自ら作ったPythonスクリプトです。理屈上は定義した変数以外はないのでメモリの消費は一定のはずですが実際はどんどん増えて使い切ってしまう現象に陥りました。いままで何事もなく動いていたので何か原因があるはずです。しかしながら数千行のコードを調べるのは時間の無駄なので別の方法を調べてみました。検索するとPythonにガーベッジコレクションの機能があり、検索で実例をみて当てはめてみました。やはりPythonでループしていると起こり得るケースです。import gc
でモジュールを読みこみループ上にgc.collect()
を配置します。Pythonのドキュメントを見るとgc.enable()
という機能があり、自動的にメモリを開放してくれるそうですが今回は適用しません。
追記:
gc.collect()
を使うと50%程度とかなり遅くなることが分かりました。シュミレーションで使う場合はgc.enable()
を使ったほうが良さそうです。いろいろアプリケーションを整理したらCactiが正常に動くようになりました。
追記2
まだPythonのバグがFIXできなかったのでデバッグをしてみました。検索すると何件かヒットしたので最初のひとつを挑戦してみました。
pip install objgraph
python3 -m pdb ./{program}.py
(Pdb) continue
CTRL+C
(Pdb) import objgraph
(Pdb) objgraph.show_most_common_types(limit=20)
function 18919
tuple 8785
dict 7327
weakref 4017
builtin_function_or_method 3585
...
(Pdb) continue
CTRL+C
(Pdb) import objgraph
(Pdb) objgraph.show_most_common_types(limit=20)
function 18903
tuple 8957
dict 7326
weakref 4018
builtin_function_or_method 3585
...
tuple
が増えてることが分かります。tuple
はあまり使っててないでたぶんモジュールかなとか推測できますがそれ以上進めません。
次にtracemalloc
を使った方法で挑戦してみました。
import tracemalloc
tracemalloc.start()
# ... run your application ...
...
snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics('lineno')
print("[ Top 10 ]")
for stat in top_stats[:10]:
print(stat)
[ Top 10 ]
/home/parvati/environments/test2_env/lib/python3.6/site-packages/mysql/connector/connection_cext.py:289: size=200 KiB, count=3633, average=56 B
/usr/lib/python3.6/tracemalloc.py:65: size=6336 B, count=88, average=72 B
/usr/lib/python3.6/tracemalloc.py:469: size=6224 B, count=91, average=68 B
...
/home/parvati/environments/test2_env/lib/python3.6/site-packages/mysql/connector/connection_cext.py:289: size=384 KiB, count=7019, average=56 B
/usr/lib/python3.6/tracemalloc.py:469: size=13.5 KiB, count=209, average=66 B
/usr/lib/python3.6/tracemalloc.py:462: size=10.1 KiB, count=208, average=50 B
...
connection_cext.py
のサイズが 200 KiBから384 KiBに増えていることがわかります。このプログラムを続けていると一定の割合で増加していきます。そこでonnection_cext.py
をエディタで開いてみました。
289行: row = self._cmysql.fetch_row()
とあります。class CMySQLConnection()
内に定義された関数です。おそらくループでクラスで定義されたオブジェクトを消去せずオブジェクトを追加していったように推測できます。さらにGoogleで検索してみるとSELECT分はメモリに残るというようなコメントも見受けられます。となるとmysql-connector-python
を編集するとなると手に負えないので、ほかpythonでMySQLが使えるモジュールを探してみてPyMySQL
をインストールしてみました。
違いは。またmysql-connector-python
はリストで返しますがPyMySQLはdict形式で返しますmysql-connector-python
に比べて半分くらいパーフォーマンスが落ちます。PyMySQL
でプログラムを直してデバッグしてみます。
[ Top 10 ]
/usr/lib/python3.6/tracemalloc.py:469: size=16.1 KiB, count=251, average=66 B
/home/parvati/environments/test2_env/lib/python3.6/site-packages/pymysql/connections.py:1211: size=14.1 KiB, count=150, average=96 B
/usr/lib/python3.6/tracemalloc.py:462: size=12.1 KiB, count=250, average=50 B
/home/parvati/environments/test2_env/lib/python3.6/site-packages/pymysql/connections.py:1209: size=9672 B, count=403, average=24 B
/usr/lib/python3.6/tracemalloc.py:65: size=9000 B, count=125, average=72 B
./binance_histricalklinev2.py:144: size=6448 B, count=86, average=75 B
/home/parvati/environments/test2_env/lib/python3.6/site-packages/pymysql/cursors.py:407: size=5032 B, count=70, average=72 B
...
[ Top 10 ]
/usr/lib/python3.6/tracemalloc.py:469: size=16.0 KiB, count=250, average=66 B
/home/parvati/environments/test2_env/lib/python3.6/site-packages/pymysql/connections.py:1211: size=14.1 KiB, count=150, average=96 B
/usr/lib/python3.6/tracemalloc.py:462: size=12.1 KiB, count=249, average=50 B
/home/parvati/environments/test2_env/lib/python3.6/site-packages/pymysql/connections.py:1209: size=9696 B, count=404, average=24 B
/usr/lib/python3.6/tracemalloc.py:65: size=9000 B, count=125, average=72 B
./binance_histricalklinev2.py:144: size=7128 B, count=95, average=75 B
/home/parvati/environments/test2_env/lib/python3.6/site-packages/pymysql/cursors.py:407: size=5176 B, count=72, average=72 B
connections.py:1209
とcursors.py:407
に若干増加が見られる程度となりました。これでしばらく様子見ようと思います。
参考:
- https://docs.python.org/3/library/gc.html#gc.enable
- https://dev.mysql.com/doc/connector-python/en/connector-python-installation-binary.html
- http://tech.labs.oliverwyman.com/blog/2008/11/14/tracing-python-memory-leaks/
- https://docs.python.org/3/library/pdb.html
- https://mg.pov.lt/objgraph/
- https://qiita.com/mitch0807/items/f9f6efd4e65a022a8ab9
- https://pymysql.readthedocs.io/en/latest/user/installation.html