LinuxのOOM Killerでプロセスが強制終了する原因と対処法|dmesgで確認してoom_score_adjで優先度を設定する方法

運営:リナックスマスター.JP(代表 宮崎智広)
HOME > Linux技術 リナックスマスター.JP(Linuxマスター.JP) > Linuxtips, Linuxトラブルシューティング > LinuxのOOM Killerでプロセスが強制終了する原因と対処法|dmesgで確認してoom_score_adjで優先度を設定する方法
「Webサーバーが正常に動いているはずなのに、突然Apacheプロセスが消えた」「cronジョブが途中で終わるのにエラーログに何も記録されない」

こういったケースで真っ先に疑うべきなのが、LinuxカーネルのOOM Killer(アウト・オブ・メモリ・キラー)だ。メモリが枯渇したとき、カーネルがシステム全体のクラッシュを防ぐために特定のプロセスを強制終了する仕組みで、実行時に通常のエラーを返さないため、カーネルログを調べなければ原因に気づかないことが多い。

この記事では、OOM Killerが発動したかどうかをログで確認する方法から、oom_score_adjを使って重要プロセスをKill対象から除外する恒久設定まで、RHEL 9.4 / Rocky Linux 9の実機出力をもとに解説する。Ubuntu 24.04 LTSでも同じ手順で使える想定だ。

この記事のポイント

・OOM Killerの発動は dmesg | grep -i "out of memory" で即確認できる
・Kill対象はoom_scoreが高いプロセス(/proc/PID/oom_scoreで確認)
・oom_score_adjを-1000にするとそのプロセスはKill対象から外れる
・systemdサービスはOOMScoreAdjust=-1000とMemoryMaxを組み合わせて永続保護する


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

なぜプロセスが突然終了するのか

OOM Killerはカーネルが実装する「最後の砦」だ。物理メモリとスワップ領域の両方が使い切られると、カーネルはシステム全体のパニックに至る前に、最もメモリを占有しているプロセスをスコアで選び出してKillする。

OOM Killerが発動する条件は次の3つが重なった状態だ:
・物理メモリが枯渇した
・スワップ領域も枯渇した(またはスワップが設定されていない)
・新しいメモリアロケーション要求がカーネルに来た

この動作は意図的な設計であり、OOM Killerが動くこと自体は正常だ。問題はKill対象として重要なサービスが選ばれてしまったとき。Apache・MySQL・Javaアプリのようにメモリを多く使うサービスほどスコアが高くなりやすく、対策なしでは「突然死」のリスクを抱えたまま運用することになる。

OOM Killerが問題になりやすい環境:
・バッチ処理やJavaアプリが一時的に大量のメモリを消費する環境
・物理メモリが2GB以下のVPSやコンテナ環境
・メモリリークが疑われる長時間稼働のプロセスが存在する場合

OOM Killerが動いたかをログで確認する

1. dmesgで確認する

カーネルのリングバッファに記録される。起動後にOOM Killerが動いた場合はここに残る。

# OOM Killer のログを確認する dmesg | grep -i "out of memory\|oom_kill\|killed process" # または "oom" だけでフィルタする(大文字小文字を区別しない) dmesg | grep -i oom

OOM Killerが発動していた場合、以下のような出力が記録されている:

# 実機出力(RHEL 9.4 / hostname: ap01.example.local) [4523867.123456] Out of memory: Killed process 8421 (java) total-vm:4194304kB, anon-rss:3829760kB, file-rss:0kB, shmem-rss:0kB, UID:0 pgtables:8192kB oom_score_adj:0 [4523867.134567] oom_reaper: reaped process 8421 (java), now anon-rss:0kB, file-rss:0kB, shmem-rss:0kB

「javaプロセス(PID:8421)がKillされた」と読み取れる。ログの見方:
・total-vm:4194304kB:プロセスの仮想メモリ総量(約4GB)
・anon-rss:3829760kB:実際に物理メモリを占有していた匿名ページ量(約3.7GB)
・oom_score_adj:0:終了時の調整値は0(デフォルト)だった
・oom_reaper:Killされたプロセスのメモリを回収するカーネルスレッドのログ

2. journalctlで確認する

再起動をまたいだ過去のログや詳細な時刻情報はjournalctlで確認できる。

# カーネルメッセージからOOM関連を抽出する journalctl -k | grep -i "out of memory\|oom_kill\|killed process" # 前回起動時のログも確認する(-b -1 で前回boot) journalctl -k -b -1 | grep -i oom # 特定の日時以降で絞り込む journalctl -k --since "2026-09-01 00:00:00" --until "2026-09-02 00:00:00"

-kフラグはカーネルログのみを表示する。--sinceと--untilで時刻を絞ることで、深夜の障害ログを素早く探せる。

どのプロセスがKill対象になるか

1. oom_scoreを確認する

稼働中の全プロセスには/proc/PID/oom_scoreというファイルがあり、OOM Killerはこのスコアを参照して対象を選択する。

# 特定プロセスのスコアを確認する(例: httpd) PID=$(pidof httpd | awk '{print $1}') cat /proc/$PID/oom_score # 全プロセスをスコア降順で一覧表示する for pid in $(ls /proc | grep -E '^[0-9]+$'); do score=$(cat /proc/$pid/oom_score 2>/dev/null) comm=$(cat /proc/$pid/comm 2>/dev/null) [ -n "$score" ] && echo "$score $pid $comm" done | sort -rn | head -10

実行例(RHEL 9.4 / 4GB RAM):

# スコアの高いプロセス TOP10 892 8421 java 345 7234 mysqld 123 5678 nginx 98 4321 httpd 12 987 sshd

この例ではjavaプロセスのスコアが飛び抜けて高く、メモリ不足が発生すれば真っ先にKillされる状況だ。

2. oom_scoreの算出ロジック

oom_scoreは主に次の要素で増加する:

・プロセスが占有している物理メモリ量(最大の要因)
・子プロセスのメモリ合算値
・スーパーユーザー(root)プロセスはごくわずかに低く算出される

「スコアが高い=メモリを多く使っている=Kill対象になりやすい」という設計だ。ApacheやMySQLのように本来重要なプロセスほどスコアが高くなりやすく、何も設定しなければ障害発生時に最初にKillされるリスクがある。

3. psでメモリ消費量上位のプロセスを確認する

OOM Killerが発動した後は「なぜメモリが枯渇したか」の特定が必要だ。psコマンドでメモリ消費量が多いプロセスを素早く把握できる。

# 物理メモリ使用量(%MEM)の降順でプロセスを表示する ps aux --sort=-%mem | head -20 USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND mysql 18423 5.2 52.3 1572864 1048576 ? Sl 03:14 102:34 /usr/sbin/mysqld apache 2341 0.3 3.1 524288 62464 ? S 00:00 0:03 /usr/sbin/httpd

%MEMが異常に高いプロセスが、メモリリークや設定ミスで際限なく消費を増やしているプロセスだ。週次で定期的に確認することで、じわじわとメモリが増加しているプロセスに早めに気づける。

4. /proc/PID/smaps_rollupでプロセスの実メモリ使用量を確認する

psの%MEMは仮想メモリ込みの概算値だ。プロセスが実際に物理メモリをどれだけ使っているかを正確に確認したい場合は/proc/PID/smaps_rollupを使う。

# mysqldプロセスの実メモリ使用量を確認する(PID=18423の場合) cat /proc/18423/smaps_rollup Rss: 1048576 kB Pss: 1048112 kB Shared_Clean: 32768 kB Shared_Dirty: 0 kB Private_Clean: 8192 kB Private_Dirty: 1040384 kB Referenced: 1044480 kB Anonymous: 1040384 kB

Private_Dirtyは他のプロセスと共有されていない変更済みメモリだ。この値が大きいプロセスはスワップアウトされにくく、OOM Killerのターゲットになりやすいという特徴がある。

重要プロセスをKillから守るoom_score_adjの設定

oom_score_adj(-1000 ~ +1000)をプロセスに設定することで、最終的なKillスコアを調整できる。

・-1000:スコアが実質ゼロ以下になり、OOM Killerの選択対象から外れる(systemd/initはデフォルトで-1000設定済み)
・-500:スコアを大幅に下げる。ほとんどの状況で生き残れる
・0(デフォルト):oom_scoreの計算値がそのまま使われる
・+1000:スコアが最大化され、優先的にKillされる(本番環境での使用は避ける)

注意:特定のプロセスを-1000で保護すると、OOM Killerはスコアが次に高い別プロセスを代わりにKillする。保護するプロセスを増やしすぎると、最終的に保護されていない重要なプロセスが落とされることがある。

1. 一時的に設定する(プロセス再起動でリセット)

# プロセスのPIDを特定する(例: httpd) PID=$(pidof httpd | awk '{print $1}') echo "対象PID: $PID" # 現在のoom_score_adjを確認する cat /proc/$PID/oom_score_adj # -1000を設定してKill対象から除外する(root権限が必要) echo -1000 > /proc/$PID/oom_score_adj # 設定を確認する cat /proc/$PID/oom_score_adj

実行例:

# 変更前 0 # 変更後 -1000

この方法はプロセスやサーバーの再起動でリセットされる点に注意すること。常駐プロセスへの適用には次のsystemd永続設定を使うのが鉄則だ。

2. systemdサービスに永続設定する

systemdで管理されているサービスには、ユニットファイルのオーバーライドで設定を永続化するのが正しいやり方だ。既存のユニットファイルを直接編集するのではなく、/etc/systemd/system/サービス名.service.d/配下にオーバーライドファイルを作成する。

# オーバーライドディレクトリを作成する(例: httpd) mkdir -p /etc/systemd/system/httpd.service.d/ # 設定ファイルを作成する(vi で編集する) vi /etc/systemd/system/httpd.service.d/oom.conf

ファイルの内容(以下を記述して保存する):

[Service] OOMScoreAdjust=-1000

設定を反映させる:

# systemdデーモンをリロードしてサービスを再起動する systemctl daemon-reload systemctl restart httpd # PIDを確認してoom_score_adjを検証する PID=$(pidof httpd | awk '{print $1}') cat /proc/$PID/oom_score_adj

実行例(RHEL 9.4):

# 確認結果 -1000

3. MemoryMaxでプロセスのメモリ上限を設定する(cgroup v2)

MemoryMaxを設定すると、そのサービスが使える物理メモリの上限を制限できる。上限に達するとそのサービス内でのみOOM Killerが動くため、他のプロセスへの連鎖を防げる。

# Apacheのメモリ上限を2GBに制限する例 mkdir -p /etc/systemd/system/httpd.service.d/ vi /etc/systemd/system/httpd.service.d/memory.conf

ファイルの内容:

[Service] MemoryMax=2G

systemctl daemon-reload systemctl restart httpd # 設定値を確認する systemctl show httpd.service -p MemoryMax MemoryMax=2147483648

MemoryMaxはcgroup v2の機能だ。RHEL 8以降・Ubuntu 20.04以降はデフォルトでcgroup v2が有効なので使える。RHEL 7など古い環境ではMemoryLimit(cgroup v1)を使うこと。

# cgroup v1環境(RHEL 7等)での書き方 [Service] MemoryLimit=2G

4. systemd以外のプロセスへの設定

cronや自作の常駐スクリプトには、起動スクリプトの先頭で設定する方法が現実的だ。$$はシェルスクリプト自身のPIDを指す。

#!/bin/bash # スクリプト起動直後に自プロセスのoom_score_adjを設定する echo -1000 > /proc/$$/oom_score_adj echo "oom_score_adjを設定: $(cat /proc/$$/oom_score_adj)" # ここから本来の処理を開始する

OOM発生を防ぐための運用設計

oom_score_adjによる優先度の調整は「重要プロセスを守る」対症療法だ。根本的にはメモリ不足そのものを解消する必要がある。

1. メモリとスワップの使用状況を把握する

# メモリとスワップの使用状況を確認する free -h # スワップの種類・サイズ・使用量を確認する swapon --show # vm.overcommit_memoryの設定を確認する # (0=ヒューリスティック, 1=常に許可, 2=制限あり) cat /proc/sys/vm/overcommit_memory

実行例(RHEL 9.4 / 4GB RAM / OOM Killer発動前の典型的な状態):

# free -h の出力 total used free shared buff/cache available Mem: 3.7Gi 3.1Gi 124Mi 38Mi 512Mi 442Mi Swap: 2.0Gi 1.8Gi 212Mi # swapon --show の出力 NAME TYPE SIZE USED PRIO /dev/sda2 partition 2.0G 1.8G -2 # vm.overcommit_memory 0

Swap使用率が90%を超えている状態はOOM Killer発動寸前のサインだ。スワップの設計と拡張手順については「Linuxサーバーのスワップ領域を設計・拡張する方法」を参照してほしい。

2. vm.swappinessでメモリ利用の傾向を調整する

vm.swappiness(0 ~ 100、デフォルト 60)は、カーネルがどの程度積極的にメモリをスワップに追い出すかを制御するパラメータだ。

# 現在のswappinessを確認する cat /proc/sys/vm/swappiness # データベースサーバーの場合は低めに設定する # /etc/sysctl.confに追記して再起動後も維持する echo "vm.swappiness=10" >> /etc/sysctl.conf sysctl -p

sarコマンドでメモリ使用率の時系列変化を継続的に監視する方法は「sarコマンドでCPU・メモリの使用率履歴を確認する方法」が詳しい。

「oom_score_adj -1000を設定したのにKillされた」場合のトラブルシュート

設定後もプロセスが突然終了する場合は、以下を順番に確認する。

・設定の反映漏れ:systemctl daemon-reloadの実行後にサービスを再起動したか確認する。複数のhttpdワーカープロセスが動いている場合、親プロセスのPIDにしか設定できていないケースがある
・cgroup OOMとの混同:コンテナやcgroup v2環境でメモリ上限を設定している場合、OOM Killerとは別の「cgroup OOM」が発動する。この場合は/proc/PID/oom_score_adjではなくcgroupのメモリ制限の見直しが必要
・SIGKILL(手動Kill)との混同:監視ツールやwatchdogスクリプトが意図せずkillを送っていないかを確認する

# systemdサービスのcgroupメモリ制限を確認する systemctl show httpd | grep -i memory # cgroup OOMの発動ログを確認する journalctl | grep -i "oom-kill\|memory.oom.group"

実行例(cgroupメモリ制限なしの場合):

# systemctl show httpd の出力(メモリ制限なし) MemoryAccounting=no MemoryMax=infinity

MemoryMax=infinityであればcgroup側のメモリ制限は設定されていない。

本記事のまとめ

やりたいこと コマンド・設定
OOM Killerの発動を確認する dmesg | grep -i "out of memory"
過去のOOMログを確認する journalctl -k -b -1 | grep -i oom
メモリ消費量上位のプロセスを確認する ps aux --sort=-%mem | head -20
プロセスのoom_scoreを確認する cat /proc/PID/oom_score
Kill対象から除外する(一時) echo -1000 > /proc/PID/oom_score_adj
systemdサービスに永続設定する ユニットファイルに OOMScoreAdjust=-1000
サービスのメモリ上限を設定する(cgroup v2) ユニットファイルに MemoryMax=2G
cgroup OOMを確認する journalctl | grep -i "oom-kill"
スワップの状態を確認する swapon --show
OOM Killerは「メモリ管理の失敗」ではなく「システムを守るための最終手段」だ。まずdmesgでKillされたプロセスとスコアを特定し、重要プロセスにはoom_score_adj=-1000を設定して保護する。さらにMemoryMaxで上限を設けることで、特定サービスの暴走が他プロセスに波及するのを防ぐことができる。その上でfree -hやスワップの使用率を定期的に監視しながら、根本的な対策(スワップ増強・アプリのメモリ最適化)につなげていくのが実務での正しい手順だ。

サーバーが急に重くなるケース全般の切り分けは「Linuxサーバーが急に重くなった時の原因切り分け手順」も参考にしてほしい。

OOM Killerの調査ができたら、次は「なぜこうなるのか」をLinux設計の視点で理解する

oom_score_adjで重要プロセスを守ることができたら、次のステップはカーネルのメモリ管理の仕組みそのものを理解することです。freeコマンドの読み方・スワップの設計・cgroup v2のメモリ制限まで体系的に把握することで、OOMが起きる前に予防できる設計力が身につきます。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、『Linuxサーバー構築入門マニュアル(図解60P)』を完全無料でプレゼントしています。

※ 「独学の時間がもったいない」「プロから直接、現場の技術を最短で学びたい」という本気の方には、2日で実務レベルのスキルが身につく【初心者向けハンズオンセミナー】も開催しています。

<PR>手元に置いて学びを深める1冊

詳解 システム・パフォーマンス 第2版

まず無料メルマガでLinuxの基礎を固めるのが第一歩、その上でOOM KillerやcgroupなどLinuxカーネルの動作原理を体系的に理解したいなら本書が定番だ。

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録

運営

リナックスマスター.JP(代表 宮崎智広)

株式会社イーネットマーキュリーが運営するLinux教育サイトです。代表の宮崎智広は現役のLinuxサーバー管理者として20年以上の実務経験を持ち、累計3,100名以上のエンジニアを指導してきました。実践型のLinuxセミナーや無料マニュアルの配布を通じてLinux人材の育成に取り組んでいます。