Skip to content
Latchkey

Memory Leak とは?二度と戻らないメモリ

memory leak は、プログラムがもう必要としないメモリを握り続け、時間とともにメモリ使用量が増え、やがて枯渇するときに起こります。

memory leak は最初は劇的な crash ではありません。ゆっくりとした上昇です。プログラムはメモリを確保し続け、その一部を決して手放さないため、使用量がじわじわと上がります。最終的にプロセスは利用可能なメモリを使い果たし、crash するか kill されます。leak は手動メモリ管理の言語(解放を忘れる)でも、garbage collection のある言語(参照を生かし続ける)でも起こります。

leak とは本当は何か

leak はまだ確保されているが、二度と使われることも解放されることもないメモリです。手動の言語では対応する free のない確保であり、garbage collection の言語では何かがまだ参照しているために collector が回収できないオブジェクトです。

一般的な原因

  • 決して切り詰められない、増え続ける cache やリスト。
  • 追加されるが決して削除されない event listener や subscription。
  • 大きなオブジェクトを捕捉して保持し続ける closure。
  • プログラムの生存期間を通じてエントリを蓄積するグローバルな registry。

なぜ leak は厄介なのか

leak が何かをすぐ壊すことはめったにありません。寿命の短いプログラムは leak が問題になる前に終わるかもしれません。しかし長時間動くサーバーはゆっくりと劣化し、やがて倒れます。多くの場合、問題のコードが動いてから何時間も後にです。

leak を見つける

heap snapshot と memory profiler は、どのオブジェクトが増え続けているか、そして何がそれらへの参照を保持しているかを示します。長時間の実行にわたってメモリを追跡し、着実な上昇傾向を探すのが、leak の存在を確認する通常の方法です。

簡単な例

リクエストごとにモジュールレベルの配列へ項目を push し、決して削除しないと、メモリが leak します。配列は永遠に増え続け、それらの項目が二度と読まれなくても、配列が保持するものは何も回収できません。

CI における memory leak

leak のある test suite は runner のメモリ上限を越えて OOM-kill されることがあり、多くの場合は大規模な実行でのみ起こるため、flaky な失敗に見えます。より大きな runner(Latchkey)は症状を先延ばしにし、一時的な OOM kill を自動 retry しますが、永続的な leak はやはり修正が必要です。メモリを増やしても crash を先送りするだけだからです。

重要なポイント

  • memory leak は決して解放されない確保済みメモリで、時間とともに使用量が増えます。
  • leak は手動と garbage collection の両方の言語で、多くの場合残り続ける参照によって起こります。
  • CI では OOM kill として表面化し、しばしば flaky な失敗と誤解されます。

関連ガイド