@momomomomomomomomomo

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

Pythonのメモリ解放について

Pythonのメモリリーについて

工場でラズパイを使いたいのですが、テスト稼働中に度々フリーズしたり
とても重くなって動きが遅くなったりしました。
夜勤もあるのでほぼ24時間連続稼働でプログラム実行しています。

メモリリークについて解決方法を教えて下さい。

発生している問題

2~3日連続稼働させていると固まる⇒再起動すると直るの繰り返し・・・
メモリリークしてるのか?と思い調べました。

「tracemalloc」を使ってメモリの使用状況を確認しました。

<frozen importlib._bootstrap_external>:729: size=46.4 MiB, count=345817, average=141 B

上記が一番多くメモリを使っているそうでプログラムでループ処理していると、
1回目、2回目、3回目・・・・とどんどん増えていきます。

配列や変数はdel文とgc.collect()でメモリ解放できるそうですが、
上記のメモリをループごとに解放する方法はありますか?

そもそもこれが何に使っているのかも解読できてません・・・

解決方法があr教えてください。

0 likes

4Answer

ループごとにメモリが溜まっていくことがわかっているのならロジックにメモリが解放されない部分が残っているということなので,まずは動かしているコードを検証すべきだと思います.

1Like

Comments

  1. コード自体はdel文等で対策できると調べて分かったのですが、質問のコードが何に使われているのか分かりませんでした。インポートしたモジュールのことだそうで解決しました。回答ありがとうございました。

言語処理系の GC の一般的な動作としては

  • 最初にメモリ使用量の閾値が設定されている
  • メモリを新たに使おうとしたときに閾値を越えてしまうなら GC が発動する (使われていないメモリを探して回収する)
  • GC を使ってもメモリが足りないなら閾値を大きくする

ということを繰り返します。 (実際にはこんな単純なことではなく色々な工夫があるのですがここではものすごく単純化しています。)

メモリ使用の総量が伸び続けているときは GC を使った上で回収できないメモリがあることを意味します。 なので gc.collect() を使って GC を明示的に起動しても無意味です。

メモリを回収できないということは不必要に参照が残っている箇所があるということですし、ロジックを精査してそういう箇所を見つけるしか仕方ないです。 ループの外から参照されているオブジェクト (配列や辞書) に追記している箇所があれば (回収できないので) 際限なくメモリ使用量が増えることになるでしょう。 そうしないように書き換える必要があります。

0Like

Comments

  1. 回答ありがとうございます。ループの外側に注意してプログラム書き直ししてみます。

コード(あるいは模擬した)を提示してもらわないと何とも言えません。

どんな言語でもそうですが、GCが実装されている言語はどこかのタイミングでプログラムを停止してメモリを整理しています。
gc.collect()はそのキッカケを作るだけで根本的な解決をしません。

  • インスタンスを毎回生成しててそれをどこかに保存している
  • Dictの多用と保持

それらを見つけて地道に改善(delなりNone代入する or そもそも使用しない)する以外無いと思います。

0Like

Comments

  1. 質問のコードはインポートしたモジュールのことで、ループの度にメモリが増えることはないそうです。となると変数や配列をdelするしかなさそうですね・・・度々フリーズするよりかはマシなので地道にやることにします。回答ありがとうございました。

<frozen importlib._bootstrap_external>:729: size=46.4 MiB, count=345817, average=141 B

これは importlib._bootstrap_external の729行目で確保されたメモリ量の合計です。 Python 3.11 系なら、ちょうどその行で .pyc ファイルのデータのアンマーシャリングをしているようです。つまりは import 文や importlib.import_module() でインポートされたモジュールのバイトコードと定数が占めるメモリ量を表します。

このメモリ量は実行中に増えることはあっても、普通は使うモジュール数に限りがある以上どこかで頭打ちになるはずです。(一度インポートしたモジュールはインメモリでキャッシュされるので、もう一度インポートしてもメモリ量は変わりません。)本当にループごとに際限なく増えているなら、ループごとに新しいソースファイルをインポートするという相当特殊な処理をしていることになります。一度インポートしたモジュールのメモリは解放できないので、インポートしないように処理を変えるしかありません。

0Like

Comments

  1. 回答ありがとうございます。インポートしたモジュールのことなんですね、メモリ量が変わらないとの事で安心しました。

  2. メモリ量が変わらないとの事で安心しました。

    普通の作りのプログラムならそうなんですが、 tracemalloc 上の数値が増えているなら実際にメモリ量は増えています。変なインポート処理をしていないか確認してください。

    ここに問題がなさそうなら tracemalloc で他にメモリ量が増えている行がないかチェックしてください。別の原因のメモリリークが疑われます。

Your answer might help someone💌