メモリ不足とOOM Killer
メモリが足りなくなると、Linuxはまずスワップに逃げて延命し、それでも足りなければカーネルがプロセスを強制終了して全滅を防ぎます。この仕組みをOOM Killerと呼びます。free の読み方、スワップの副作用、誰が・なぜ殺されたのかをログから読み解く手順を身につけると、突然アプリが落ちる障害にも慌てず対応できます。
「さっきまで動いていたアプリが、いつの間にか落ちている」――ログにエラーもなく、ただ消えている。これはメモリ不足が原因であることが多い障害だ。
CPUの過負荷なら重くなるだけで動き続けることが多いが、メモリはゼロになると話が変わる。空きが尽きると、カーネルは生き残りをかけてプロセスを殺しにかかる。この仕組みを知らないと、原因不明の突然死に見えてしまう。
📉 free -h の読み方——本命は available
メモリの空き具合を見る基本コマンドが free -h だ。だが、この出力にはよくある誤読ポイントがある。
free -h を打つと total・used・free・shared・buff/cache・available という列が並ぶ。多くの初学者は free(空き)の数値が小さいと「メモリが足りない」と早合点してしまう。
buff/cache は、Linuxがディスクの読み書きを高速化するために空きメモリを一時的にキャッシュとして使っている分だ。これはアプリが必要とすればすぐに手放される、いわば「今は空いているので有効活用中」の領域である。
そのため本当に見るべきは available 列だ。これは「buff/cache を解放した場合も含めて、新しいプロセスがすぐに使える見込みのメモリ量」をカーネルが計算した値になる。available が小さくなってきたときこそ、本当の意味でメモリが逼迫している。
逆に言えば、free の値がほぼゼロでも available が十分にあれば健全な状態だ。Linuxは空きメモリを遊ばせずキャッシュに回すのが標準動作なので、free が小さいこと自体は異常ではない。
🐌 スワップに逃げると、遅くなる
available も心もとなくなってくると、Linuxは次の手として スワップ(tbl-swap) を使う。物理メモリの中身の一部をディスク上の領域に一時退避し、空きを作る仕組みだ。
スワップのおかげで、多少のメモリ不足ならプロセスを殺さずに済む。だが代償は大きい。ディスクはRAMに比べて桁違いに遅く、スワップの読み書き(swap-in / swap-out)が頻発すると、体感速度が急激に悪化する。
free -h の Swap 行の used が増え続けている、あるいは vmstat の si/so(swap-in/swap-out)の値が高止まりしているようなら、スワップに頼り切った危険な状態だと判断できる。
ここで気をつけたいのは、スワップが「あるだけで悪」というわけではないという点だ。ごく軽微な、めったに使わないメモリ領域が少しだけスワップへ追い出される程度なら、体感速度への影響はほとんどない。問題視すべきは、頻繁に読み書きが発生し続ける「スラッシング」状態のほうだ。
vmstat 1 を数十秒流して si/so の列を眺めると、スワップが一時的な現象か慢性的な現象かを見分けやすい。ときどき数値が出る程度なら様子見でよいが、継続的に高い値が出るなら要注意のサインだ。
☠ OOM Killer——最後の砦
スワップも使い切り、それでもメモリ要求に応えられなくなると、カーネルは最終手段に出る。これがOOM Killer(tbl-oom)だ。
OOM Killerは、システム全体がメモリ枯渇で完全に応答不能になる前に、いくつかのプロセスを選んで強制終了し、残りのシステムを生かそうとする仕組みだ。全滅を防ぐための、いわば緊急避難措置である。
どのプロセスを終了させるかは、カーネルが内部で計算する「OOMスコア」という指標にもとづいて決まる。多くのメモリを使っているプロセスほどスコアが高くなりやすく、優先的に狙われる傾向がある。逆に、OSの根幹を支える重要なプロセスにはスコアを下げる調整が入っており、めったなことでは狙われない設計になっている。
🔍 dmesg・journalctl -k で足跡を探す
アプリが原因不明に落ちたら、まずOOM Killerが動いていないかを疑い、ログで裏を取る。
カーネルのメッセージは dmesg か journalctl -k で見られる。ここに「Out of memory: Killed process」という文字列があれば、それがOOM Killerの実行記録だ。
📖 誰が・なぜ殺されたかを読む
OOM Killerのログには手がかりが詰まっている。Killed process の行には、殺されたプロセスのPID・名前・使用メモリ量(total-vm や rss)が記録される。
PIDと名前で「何が」殺されたかが分かり、total-vm・rss の値で「どれだけメモリを抱えていたか」が分かる。同じログの少し上には、殺害の判断に至った直前のメモリ使用状況も記録されていることが多く、なぜ枯渇に至ったのかの手がかりになる。
殺されたのが特定の1プロセスだけで、他は普段どおりなら、そのプロセスのメモリリークや一時的な負荷急増が疑わしい。逆に複数プロセスが順に殺されているなら、システム全体が慢性的にメモリ不足である可能性が高い。
🛠 対処——増設・swap・アプリ設定・cgroup
原因が読めたら、対処の選択肢はいくつかある。
根本的な解決は物理メモリの増設だが、すぐには対応できないことも多い。応急処置としてはスワップ領域を増やす方法がある。スワップは遅いが、突然のプロセス強制終了よりはましな場合が多い。
クラウド上の仮想マシンなどですぐにメモリを増設できない場合、swapfileと呼ばれる普通のファイルを一時的なスワップ領域として追加する手がよく使われる。ディスクに空きさえあれば、再起動せずにその場でメモリの逃げ場を増やせる、手軽な延命策になる。
アプリ側の設定でメモリ使用量の上限を絞る、キャッシュサイズを調整する、といった対処も有効だ。データベースやWebサーバーには、たいていメモリ使用量に関する設定項目がある。
より踏み込んだ対処が cgroup(コントロールグループ)によるメモリ制限だ。プロセスのグループごとにメモリ使用量の上限を割り当てておけば、あるアプリが暴走しても他のプロセスを巻き込まずに済む。コンテナ技術の多くもこの仕組みを利用している。
突然の落ち方に出会ったら、慌てず free -h で available を見て、dmesg や journalctl -k でOOM Killerの足跡を探す――この流れを押さえておけば、原因不明に見える障害も筋道立てて説明できるようになる。