メモリリーク: 静かなる暗殺者
サーバーはこうして死ぬ。長期稼働システムの天敵。
メモリリーク
確保したメモリを解放し忘れること。メモリ残量が減っていき、最終的にシステムがクラッシュする。
OOM Killer
Linuxの機能。メモリ不足になったとき、メモリを食っているプロセスを強制終了させる暗殺者。
返却忘れの末路
「メモリリーク」とは、malloc で借りたメモリを free
で返し忘れることです。プログラム自体は正常に動き続けるため、テストでは見つけにくいのが厄介です。
しかし、稼働時間が長くなるにつれて少しずつメモリ使用量が増えていき、ある日突然、サーバーが応答しなくなったり、OSの「OOM Killer」によってプロセスが強制終了されたりします。
リークの仕組み
void leak() { // 1. 確保する char *ptr = malloc(100);
// 2. 使う // ...
// 3. 忘れる (freeしない!) // 関数が終わると ptr 変数は消えるが、 // ヒープに確保された100バイトは誰にも返せずに残り続ける。}
int main() { while(1) leak(); // メモリを食いつくすまで止まらない}実践テクニック
goto cleanup イディオム
関数内で複数のリソース(ファイル、メモリ、ソケットなど)を確保する場合、エラーが起きるたびに個別に解放するのは大変ですし、バグの元です。
// gotoを使ったクリーンアップ・パターン// C言語のエラー処理の鉄板。Linuxカーネルでも多用される。
int process_file(const char *filename) { FILE *fp = fopen(filename, "r"); if (!fp) return -1;
char *buf = malloc(1024); if (!buf) { // fpを閉じてから帰らないといけない fclose(fp); return -1; }
// ... 処理 ... if (error_happened) { goto cleanup; // 共通の終了処理へ飛ぶ }
// 正常終了 free(buf); fclose(fp); return 0;
cleanup: // エラー時の後始末を一箇所にまとめる if (buf) free(buf); if (fp) fclose(fp); return -1;}演習課題
課題1: 無限ループ
わざとメモリリークするループを書き、タスクマネージャを見ながらメモリ使用量が増えていくのを確認してください(確認したらすぐ停止すること)。
合格ライン
長時間稼働するシステムでリークが致命的な理由を知っている
エラーハンドリング時の解放漏れに注意できる