開発環境におけるメモリ調査経緯の記録

主要な環境のうち開発環境の一部において、メモリ使用率が高く見える状況があったため、本当に不足しているのかを検証した。この環境は Debian 13 で物理メモリについては他より容量が少なく 2GB が実装されている。グラフ上は逼迫して見えても、Linux カーネルは空きメモリを積極的にキャッシュに使うため、見た目だけでは判断できない。本稿では、疑念を持ってから終了判定に至るまでに実際に打鍵したコマンドとその読み取りの経緯を記録する。


調査の起点

Munin のメモリグラフで常時 85〜90% 程度の使用が表示されていた。スワップも数百 MB が使われており、視覚的には「メモリがかつかつ」に見えた。そこで、以下を確かめるために調査を開始した。

  • 本当に実メモリが不足しているのか
  • キャッシュを含めた表示のため高く見えているだけなのか
  • スワップが逼迫ではなく整理として使われているのか

1. 基本状態の確認

free -h
vmstat 1 5

free の結果では、total は約 1.9 GiB、used は約 530 MiB、free は約 850 MiB、buff/cache は約 780 MiB、available は約 1.4 GiB であった。swap は 2.0 GiB 中 約 190 MiB が使用されていた。

vmstat では si と so が 0 で推移し、スワップ入出力が発生していないことを確認した。CPU idle も 98〜100% に張り付いており、負荷起因でメモリが詰まっているわけではないと判断できる。


2. プロセス別の使用量を確認する

ps aux --sort=-%mem | head -20

メモリを多く使っていたのは次のプロセスである。

  • systemd-journald 約 160 MB
  • mariadbd 約 110 MB
  • fail2ban-server 約 76 MB
  • php-fpm の各プロセス 約 40 MB
  • memcached 約 14 MB
  • apache2 のワーカプロセス群 数十 MB

いずれも常駐サービスとして想定範囲内であり、単体で異常な膨張をしているプロセスはなかった。この時点で「特定プロセスのリークが原因」という線はほぼ消えた。


3. スワップの実体を調べる

次に、実際にどのプロセスがスワップを使っているのかを /proc から直接読んだ。

grep -H VmSwap /proc/[0-9]*/status | sort -k2 -n | tail -20

この結果、mariadbd が約 92 MB、memcached が約 13 MB をスワップに退避していることが分かった。その他のプロセスは 1〜8 MB 程度であり、いずれも「長くアクセスしていないページを後ろに回した」程度である。

さらに、スワップ使用量の総計を確認した。

grep VmSwap /proc/[0-9]*/status 2>/dev/null | awk '{ sum += $2 } END { print sum/1024 " MB" }

ここで約 166 MB という値が得られた。これは swapon で見えた使用量とおおむね一致している。

swapon --show

これらにより、スワップが「メモリ不足であふれて書いている」のではなく「使っていないページを少しだけ後ろへ退避している」状態であると判定できた。


4. メモリ構成をカーネルから読む

メモリがキャッシュで膨らんでいるのかを確かめるため、/proc/meminfo を抽出した。

cat /proc/meminfo | grep -E 'MemTotal|MemFree|Buffers|Cached|SReclaimable|Shmem'

Cached と SReclaimable を足すと 700 MB 台後半となり、全体の 3〜4 割がキャッシュであることが分かった。Linux は空きメモリを遊ばせるよりキャッシュで埋める設計であるため、この状態は正常である。


5. カーネルのメモリポリシーを確認する

スワップに対してどの程度積極的か、キャッシュをどの程度落とすかは vm パラメータで決まるため、ここも調査した。

cat /proc/sys/vm/swappiness
cat /proc/sys/vm/vfs_cache_pressure

swappiness は 60、vfs_cache_pressure は 100 であった。これは Debian の標準的な値であり、管理者が特別なチューニングをしていないことを示す。したがって今回の挙動は「デフォルトポリシーのままでこうなっている」と説明できる。


6. ページフォルトとスワップ履歴を確認する

現在は落ち着いていても、過去にスワップが激しく動いていた可能性はある。そこで、ページフォルトとスワップに関係するカウンタを /proc/vmstat から抜き出した。

cat /proc/vmstat | grep -E 'pgfault|pgmajfault|pswpin|pswpout'

pswpin と pswpout は 40 万回台であったが、これは起動以来の累積値である。vmstat 実行中は si/so が 0 であったため、現在はスワップ入出力が発生していない。pgmajfault も総ページフォルトに対してはごく小さく、RAM 内でほぼ完結していると判断した。


7. 時系列の安定性を確認する

メモリが一定に保たれているか、周期的なスパイクがないかを確認するために sar を使った。

sar -r 1 5

1 秒ごとに 5 回採取したところ、kbavail は 1.47 GB 前後で安定しており、%memused も 14% 台で揺れなかった。これにより「一時的に解放されたのではなく、もともと余裕がある」ことが確認できた。


8. 実行ファイルのキャッシュ挙動を確認する

頻繁に使う /bin や /usr/bin のバイナリが、呼び出すたびにストレージを読んでいるのではないかという疑問も合わせて検証した。まずは対象ファイルの配置を確認した。

sudo filefrag -v /bin/ls

/bin/ls は 1 extent のみであり、断片化していなかった。さらにページキャッシュに載っているかを確認するために /proc/kpageflags を参照しようとした。

sudo grep $(sudo filefrag -v /bin/ls | awk '/physical/{print $4; exit}') /proc/kpageflags

このときは該当なしとなり、この瞬間はキャッシュ外であることが分かった。ただしこれは「使われていなかったから落ちていただけ」であり、一度実行すれば再びキャッシュに載る。

以上により、/bin 以下のよく使うコマンドは一度実行されればページキャッシュに載ってディスクを再度読まないこと、そして読み取りは SSD の摩耗要因にはならないことを確認できた。摩耗の主因は書き込みである。


9. ここまでの読み取り

ここまでの結果をまとめると次のようになる。

  • free と vmstat の段階で available が大きく、実メモリは足りている。
  • スワップは 190 MB 弱を静的に使っているだけで、si/so は動いていない。
  • /proc から集計した VmSwap の合計と swapon の表示が整合しており、観測は正しい。
  • /proc/meminfo から、キャッシュが見かけ上の使用率を押し上げていることが確認できた。
  • カーネルパラメータはデフォルトであり、特別にスワップを強制する設定にはなっていない。
  • sar による時系列でも安定しており、瞬間的な逼迫ではなかった。
  • 実行ファイルの読み取りが SSD を摩耗させているわけでもなかった。

総合評価

観点 状況 評価
実メモリ available 約 1.4 GiB を維持 余裕あり
スワップ I/O vmstat で常時 0 逼迫ではない
スワップ使用量 約 190 MB(MariaDB が中心) 長期アイドル整理で正常
キャッシュ 700 MB 台後半 Linux の設計どおり
カーネル設定 swappiness 60, vfs_cache_pressure 100 標準
負荷 CPU idle 98〜100% ボトルネックなし

結論と今後の観測ポイント

この Debian 13 の RAM 2GB 開発環境は、現状のサービス構成に対して不足していない。Munin による高い使用率表示はキャッシュを含んでいるだけであり、実メモリは十分に残っている。したがってカーネルが保持しているファイルキャッシュを定期的に手動で消すような運用や過度なチューニングは不要である。なおカーネルのキャッシュを人為的に消すには以下のような操作をする。

sudo sh -c 'echo 3 > /proc/sys/vm/drop_caches'

一方で、状況が変化した場合に備え、次のコマンドを定点で実行すれば同様の診断がすぐにできる。

free -h
vmstat 1 5
ps aux --sort=-%mem | head -20
grep -H VmSwap /proc/[0-9]*/status | sort -k2 -n | tail -20
grep VmSwap /proc/[0-9]*/status 2>/dev/null | awk '{ sum += $2 } END { print sum/1024 " MB" }
cat /proc/meminfo | grep -E 'MemTotal|MemFree|Buffers|Cached|SReclaimable|Shmem'
cat /proc/vmstat | grep -E 'pgfault|pgmajfault|pswpin|pswpout'
sar -r 1 5
swapon --show

これらを実行しておけば、次に「メモリが足りないのでは」という懸念が生じても、原因がキャッシュか本当の逼迫かをすぐに切り分けられる。