OOM killer 発動時の対処 - メモリ不足でプロセスが落ちた
OOM killer とは何か?
OOM killer(Out-Of-Memory killer)はLinuxカーネルに組み込まれたメモリ管理機構で、物理メモリとスワップが枯渇した際にプロセスを強制終了してシステム全体のクラッシュを防ぐ。「突然プロセスが落ちた」「ログに何も残っていない」ときはまず OOM killer の発動を疑う。
症状のチェックポイント
- アプリが理由不明でクラッシュした
systemctl statusでexit-code=SIGKILLが表示されるdmesgにOut of memory: Killed processの行がある
ログで OOM killer 発動を確認する方法
カーネルが OOM killer を発動すると、その記録は必ずカーネルリングバッファとシステムログに残る。最初にここを確認する。
dmesg で確認する
sudo dmesg | grep -i "out of memory" sudo dmesg | grep -i "oom"
[1234567.890] Out of memory: Killed process 12345 (nginx) total-vm:512000kB, anon-rss:256000kB, file-rss:8000kB, shmem-rss:0kB, UID:0 pgtables:512kB oom_score_adj:0
journalctl でカーネルログを検索する
sudo journalctl -k --since "2 hours ago" | grep -i oom sudo journalctl -k -g "Out of memory"
-k はカーネルメッセージのみ表示するオプション。時刻が絞れる場合は --since で範囲を狭める。
syslog / messages から確認する
sudo grep -i "out of memory" /var/log/syslog sudo grep -i "oom_killer" /var/log/kern.log
dmesg のタイムスタンプはブート後秒数のため、dmesg -T を使うと人間が読める日時で表示される。
どのプロセスが殺されたか調べる
ログには殺されたプロセスの情報が詳細に記録される。
sudo dmesg | grep "Killed process"
[1234567.890] Killed process 12345 (nginx) total-vm:512000kB, anon-rss:256000kB
出力の読み方:
| フィールド | 説明 |
|---|---|
12345 |
プロセス PID |
(nginx) |
プロセス名 |
total-vm |
仮想メモリ確保量 |
anon-rss |
実際に使用していた匿名ページ |
file-rss |
ファイルバックドページ |
oom_score_adj |
kill 優先度の調整値(0 がデフォルト) |
OOM killer 発動前後のメモリ状態も記録される場合がある:
sudo dmesg | grep -A 30 "Out of memory" | head -50
OOM score の仕組みとスコア確認
カーネルは各プロセスに oom_score(0〜1000)を付けており、スコアが高いプロセスから順に kill 対象として選ぶ。スコアはメモリ使用量ベースで計算される。
現在のスコア確認
cat /proc/<PID>/oom_score
全プロセスのスコアをスコア降順で一覧する:
awk '{print $1}' /proc/*/status 2>/dev/null | \
xargs -I{} sh -c 'echo "$(cat /proc/{}/oom_score 2>/dev/null) {} $(cat /proc/{}/comm 2>/dev/null)"' | \
sort -rn | head -20oom_score_adj でスコアを補正する
/proc/<PID>/oom_score_adj に -1000〜1000 の値を書き込むことでスコアを補正できる。
| 値 | 効果 |
|---|---|
-1000 |
OOM kill 対象から完全除外 |
-500 |
kill されにくくなる |
0 |
デフォルト |
500 |
kill されやすくなる |
1000 |
最優先で kill される |
# nginx プロセスを OOM kill 対象から外す(一時的) echo -500 | sudo tee /proc/$(pgrep nginx)/oom_score_adj
-1000 に設定したプロセスは OOM killer に殺されなくなるため、そのプロセス自体がメモリリークを起こすとシステム全体がクラッシュするリスクがある。重要なシステムサービスに限定して使用すること。
即時対処:プロセス再起動とメモリ回収
OOM killer がプロセスを殺した直後は、システムのメモリが回復しているはずだが、根本原因が残っている場合は再発する。
殺されたサービスを再起動する
sudo systemctl restart <service-name>
ページキャッシュを手動解放する
sync echo 3 | sudo tee /proc/sys/vm/drop_caches
drop_caches はファイルシステムのページキャッシュ・dentryキャッシュ・inodeキャッシュを解放する。本番環境での使用はI/Oスパイクを引き起こす可能性があるため慎重に。
大量メモリを使用しているプロセスを確認する
ps aux --sort=-%mem | head -20 free -h
再発防止 — oom_score_adj を永続化する
プロセスを再起動するたびに oom_score_adj はリセットされる。永続化するには systemd のユニットファイルに設定を追加する。
sudo systemctl edit <service-name>
エディタが開いたら以下を追記する:
[Service]
OOMScoreAdjust=-500
変更を反映する:
sudo systemctl daemon-reload sudo systemctl restart <service-name>
確認:
cat /proc/$(pgrep <service>)/oom_score_adj
スワップを追加してメモリ圧迫を緩和する
スワップが不足または存在しない環境では、OOM killer が発動しやすくなる。スワップファイルを追加することで急激なメモリスパイク時のバッファを確保できる。
スワップファイルの作成手順
# 2GB のスワップファイルを作成する sudo fallocate -l 2G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile
有効化を確認する:
free -h swapon --show
total used free shared buff/cache available Mem: 3.8G 3.1G 100M 50M 600M 600M Swap: 2.0G 0B 2.0G
再起動後も有効にする(/etc/fstab に追加)
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
スワップは根本解決ではない
スワップはメモリ枯渇の緩衝材に過ぎない。アプリのメモリ使用量が持続的に増加し続けるならメモリリークの調査が必要。swappiness の値(デフォルト 60)も運用環境に応じて調整する(サーバーは 10〜20 が一般的)。
根本対策:メモリ使用量を見直す
OOM killer が繰り返し発動する場合、スコア調整やスワップ追加は対症療法に過ぎない。根本原因の調査が必要。
よくある根本原因
- メモリリーク — プロセスのメモリ使用量が時間とともに増加し続ける
- 設定値の過大なメモリ要求 — JVM の
-Xmx、MySQL のinnodb_buffer_pool_size等が物理メモリを超えている - 並列プロセス数の多すぎ — Apache/PHP-FPM の
MaxRequestWorkers等
メモリ使用量の経時変化を監視する
# 5秒間隔で特定プロセスのメモリ使用量を記録する watch -n 5 "ps -p <PID> -o pid,rss,vsz,comm"
cgroup でメモリ上限を設定する(再発防止)
systemd 経由でサービスのメモリ上限を明示的に設定できる。
sudo systemctl edit <service-name>
[Service]
MemoryMax=512M
MemorySwapMax=0
上限を超えた場合、サービスのみが終了し他のプロセスへの影響を防げる。