「サーバの応答が異常に遅いのに、CPU使用率を見ると7〜14%しか使っていない」——先日、筆者がクライアントのクラウドサーバで実際に遭遇した症状です。結論から言うと、この「CPUは暇なのに重い」パターンの典型はメモリ枯渇によるスラッシングです。CPUが働けないのではなく、メモリ待ちのI/Oで全プロセスが足止めされているため、CPU使用率には現れません。この記事では、筆者が実際に行った診断(free / uptime / PSI / OOMログの4点セット)と、再起動なしで効く応急処置(スワップファイル追加)、そして恒久策への持って行き方までを、実例の数値つきで解説します。
[目次を開く]
1. 症状の特徴 — 「CPUは暇なのに重い」
まず、この問題には分かりやすい特徴があります。
- 体感は明らかに重い。SSHのログインにすら数秒〜数十秒かかる、Webの応答がタイムアウトする
- なのにCPU使用率は低い。
topで見ても平均7〜14%程度で、CPUを食い潰しているプロセスが見当たらない - ロードアベレージだけが異常に高い。vCPU 1〜2のサーバでloadが3を超えている
「CPUが暇ならボトルネックはCPU以外にある」——これが調査の出発点です。CPU使用率が低いからといって「サーバは健全」と判断するのは早計で、むしろ低いCPU使用率と高いロードアベレージの組み合わせは危険信号です。
2. なぜCPUが暇なのに重いのか — スラッシングの仕組み
メモリが枯渇すると、Linuxカーネルは物理メモリを空けるためにページ(メモリの管理単位)を追い出します。スワップが未設定の場合、追い出せるのは主に「ディスクから読み直せるページ」、つまり実行中のプログラムのコードページです。
ところが、追い出されたコードは実行のたびにディスクから読み直されます。読み直した矢先にまた別のページが追い出され、また読み直し……という無限の往復が発生します。これがスラッシングです。
flowchart TD
a["メモリ不足"] --> b["カーネルがコードページを追い出す"]
b --> c["プログラム実行時にページが無い"]
c --> d["ディスクから読み直し (I/O発生)"]
d --> e["I/O待ちでプロセス停止 (D state)"]
e --> f["CPUは待つだけで暇"]
f --> a このとき各プロセスはI/O待ち(割り込み不能スリープ、いわゆるD state)で止まっています。CPUは仕事をしていないので使用率は低いまま。しかしプロセスは大量に「実行待ち」で滞留しているため、ロードアベレージは跳ね上がります。
ここで押さえておきたい一般知識として、ロードアベレージは「実行中+実行待ち+割り込み不能スリープ(D state)のプロセス数」の平均であり、CPU使用率とは別の指標です。だからこそ「CPUは暇なのにloadが高い」という一見矛盾した状態が成立します。
3. 診断4点セット — コマンドと読み方
筆者が実際の調査で使った4つのコマンドです。この4点で証拠を揃えると、推測ではなく確定診断として報告できます。
3-1. free -m — 空きメモリとスワップの有無
free -m total used free shared buff/cache available
Mem: 981 890 45 10 46 38
Swap: 0 0 0 見るポイントは2つです。availableが数十MBまで枯れていること、そしてSwapの行がすべて0(=スワップ未設定)であること。この2つが揃っていたら、逃げ場のないメモリ枯渇です。
3-2. uptime — ロードアベレージ
uptime 14:02:11 up 32 days, 1:17, 1 user, load average: 3.21, 2.98, 2.75 このサーバはvCPU 1でした。目安として、ロードアベレージがvCPU数を大きく超えていたら処理が滞留しています。vCPU 1でload 3.2は「常時2件以上が待たされている」状態。CPU使用率が低いのにこの数値なら、待ち先はCPUではなくI/Oです。
3-3. cat /proc/pressure/memory — PSI(決定打)
cat /proc/pressure/memory some avg10=22.63 avg60=19.40 avg300=18.12 total=482910422
full avg10=8.41 avg60=7.02 avg300=6.55 total=190882145 PSI(Pressure Stall Information)はLinux 4.20以降で使えるカーネルの指標で、「メモリ待ちでプロセスが停止していた時間の割合」を直接教えてくれます。
-
some: 少なくとも1つのプロセスがメモリ待ちで止まっていた時間割合 -
full: 全プロセスがメモリ待ちで止まっていた時間割合
上の例では、直近10秒平均で22.6%の時間、誰かがメモリ待ちで停止し、8.4%の時間はシステム全体が完全に固まっていたことになります。健全なサーバではどちらもほぼ0%です。「体感の重さ」を数値で示せる、報告書に最も効く証拠です。
3-4. journalctl -k | grep -i oom — OOM Killerの発動履歴
journalctl -k | grep -i oom kernel: Out of memory: Killed process 21437 (node) total-vm:1204512kB, anon-rss:412288kB OOM Killerの発動履歴が出れば、カーネル自身が「メモリが足りずプロセスを強制終了した」と証言していることになります。「たまにプロセスが勝手に落ちている」という別の症状もこれで説明がつきます。
この4点に加えて、vmstatやiostatでスワップ未設定なのに常時ディスク読み込みが観測されれば、「コードページの追い出し→読み直し」のスラッシングと確定できます。コマンドの基本的な使い方はLinuxの基本コマンドまとめも参考にしてください。
4. 応急処置 — スワップファイルの追加(再起動不要・可逆・無料)
根本原因はメモリ不足なので恒久策はメモリ増強ですが、インスタンスタイプの変更には再起動(ダウンタイム)と費用増が伴い、その場では決められないことが多いです。そこで筆者がまず打ったのが、スワップファイルの追加です。再起動不要・いつでも外せる・追加費用ゼロと、応急処置として理想的な性質を持っています。
sudo dd if=/dev/zero of=/swapfile bs=1M count=4096
sudo chmod 600 /swapfile && sudo mkswap /swapfile && sudo swapon /swapfile
# 永続化: /etc/fstab に「/swapfile none swap sw 0 0」を追記(事前にfstabをバックアップ) ポイントは3つです。
-
count=4096で4GBのスワップファイルを作成(ディスク空き容量を先に確認) -
chmod 600は必須。スワップにはメモリ内容が書き出されるため、他ユーザーに読ませてはいけません -
swaponした時点で即座に有効。ただしこれだけでは再起動で消えるので、/etc/fstabへの追記で永続化します。fstabは書き損じると起動不能になり得るファイルなので、必ずバックアップを取ってから編集してください
スワップがあることで、カーネルは「使用頻度の低いデータページ」を退避先に逃がせるようになり、頻繁に使うコードページを追い出さずに済みます。これでスラッシングの悪循環が断ち切れます。
5. 改善の確認 — 適用直後に数値で示す
応急処置は「効いたことを数値で確認して報告する」までがワンセットです。筆者のケースでは、スワップ適用から数分後に同じコマンドを再実行し、次の改善を確認しました。
| 指標 | 適用前 | 適用後 |
|---|---|---|
| ロードアベレージ | 3.2 | 0.8 |
| PSI some avg | 22.6% | 8.2% |
loadはvCPU数を下回る水準まで低下し、メモリ待ちの停止時間も大幅に減少。SSHやWebの応答も体感で明確に改善しました。この「前後比較の数値」があると、次のステップである恒久策の提案に説得力が生まれます。
6. 恒久策への持って行き方
強調しておきたいのは、スワップはあくまで応急処置だということです。スワップはディスクをメモリ代わりに使う仕組みなので、根本的にメモリが足りない状況が続けば、いずれスワップI/O自体が性能の足かせになります。
恒久策はメモリ増強、クラウドであればインスタンスタイプの変更です。ただしこれには次の2点が伴うため、エンジニアが勝手に決めるのではなく、意思決定者に判断を回すのが正しい進め方です。
- ダウンタイム: インスタンスタイプ変更には停止・起動が必要
- 費用増: 月額のランニングコストが上がる
筆者は「現状の診断結果(4点セットの証拠)」「応急処置とその効果(前後の数値)」「恒久策の選択肢とコスト・停止時間」をまとめて提示し、判断を依頼する形にしました。応急処置で当面の火は消えているので、落ち着いて費用対効果を検討してもらえます。
7. よくある質問
Q. スワップを追加すると逆に遅くなると聞きましたが?
スワップI/Oが常態化すれば遅くなるのは事実です。ただし「スワップなしでコードページがスラッシングしている」状態よりは大幅にマシで、実測でもload 3.2→0.8と改善しています。スワップ使用量が恒常的に多いなら、それはメモリ増強のサインと捉えてください。
Q. PSIが確認できません(/proc/pressure が無い)
PSIはLinuxカーネル4.20以降の機能です。古いカーネルでは使えないため、その場合はvmstat 1のsi/so(スワップイン/アウト)やb列(割り込み不能スリープのプロセス数)、free、OOMログの組み合わせで代替診断します。
Q. OOM Killerのログが無ければメモリ枯渇ではない?
いいえ。OOM Killerは「完全に割り当て不能になった」最終局面で発動するもので、その手前のスラッシング段階では発動しないことも多いです。OOMログは「あれば強い証拠」であって、無いことは否定材料になりません。PSIとfreeを主証拠にしてください。
Q. スワップサイズはどのくらいにすべき?
応急処置としては物理メモリの1〜4倍程度、ディスク空き容量と相談して決めれば十分です。今回は1GBメモリのサーバに4GBを割り当てました。あくまで時間稼ぎなので、サイズの厳密な最適化より恒久策の検討を優先しましょう。
8. まとめ
- 「重いのにCPU使用率が低い(7〜14%程度)」はメモリスラッシングの典型症状
- 診断は4点セット:
free -m(残量とスワップ有無)、uptime(load)、/proc/pressure/memory(PSI)、journalctl -k | grep -i oom(OOM履歴) - ロードアベレージは「実行中+実行待ち+D state」の平均で、CPU使用率とは別物
- 応急処置はスワップファイル追加。再起動不要・可逆・無料で、実測でload 3.2→0.8、PSI some 22.6%→8.2%に改善
- スワップは時間稼ぎ。恒久策はメモリ増強で、ダウンタイムと費用を添えて意思決定者に判断を回す
CPU使用率だけを見て「サーバは元気」と誤診しないこと。PSIという強力な物差しを手に入れておけば、「なんとなく重い」を数値で語れるようになります。
