freeが少ないのはメモリ不足ではない
free -hでメモリの状況を確認すると、free列がほとんど残っていない状態をよく見かける。
$ free -h
total used free shared buff/cache available
Mem: 5.8Gi 476Mi 295Mi 700Ki 5.2Gi 5.3Gi
Swap: 0B 0B 0B
freeが295Miしかないため空きメモリが枯渇しているように見えるが、availableは5.3Giある。
Linuxは余ったメモリをディスクキャッシュとして使い切る方針で動くため、稼働時間の長いサーバーほどfreeは小さくなる。
メモリの余裕を判断する列はfreeではなくavailableである。
各列の意味
freeの各列は/proc/meminfoの値から算出される。
| 列 | 意味 | 対応する/proc/meminfoの値 |
|---|---|---|
| total | 搭載メモリの総量 | MemTotal |
| used | 使用中のメモリ | total - available |
| free | どこにも使われていないメモリ | MemFree |
| shared | tmpfsや共有メモリの使用量 | Shmem |
| buff/cache | ディスクキャッシュとカーネルの回収可能な領域 | Buffers + Cached + SReclaimable |
| available | 新しくプロセスを起動するときに使える見込みのメモリ | MemAvailable |
usedがtotal - availableで計算されるのはprocps-ng 4.0.1以降である。
それ以前はtotal - free - buffers - cacheで計算されていたため、同じ状態でも古いディストリビューションではusedの値が小さく出る。
バージョンはfree --versionで確認できる。
$ free --version
free from procps-ng 4.0.4
buff/cacheは必要になれば解放される
buff/cacheはディスクから読み書きした内容をメモリ上に保持しているだけの領域で、メモリが足りなくなればカーネルが自動で捨てる。
そのためavailableにはbuff/cacheの大部分が加算される。
2GiBのファイルを書き込んでfreeとavailableの動きを比較する。
$ free -h
total used free shared buff/cache available
Mem: 5.8Gi 349Mi 5.4Gi 764Ki 157Mi 5.4Gi
$ dd if=/dev/zero of=/tmp/bigfile bs=1M count=2048
$ sync
$ free -h
total used free shared buff/cache available
Mem: 5.8Gi 394Mi 3.3Gi 764Ki 2.2Gi 5.4Gi
freeは5.4Gi→3.3Giと2GiB減ったが、availableは5.4Giのまま変わらない。
書き込んだ2GiBはページキャッシュとしてbuff/cacheに積まれただけで、必要になれば解放されるためである。
availableはキャッシュ全体を足しているわけではない
availableはカーネルが/proc/meminfoのMemAvailableとして計算した見積り値で、以下のように求められる。
MemFreeから、システムが動作を維持するために確保しておく分(low watermark)を引く- ファイルキャッシュを加算する。ただしキャッシュの半分程度は解放できないものとみなして差し引く
- 回収可能なスラブ(SReclaimable)を同様に半分程度差し引いて加算する
キャッシュを保守的に見積もっているため、availableがfree + buff/cacheより小さくなるのは正常である。
逆にいえばavailableはスワップを発生させずに確保できるメモリ量の目安として使える。
buff/cacheが大きいのにavailableが増えない場合
buff/cacheの大きさに対してavailableが増えないこともある。
代表例がtmpfs(/dev/shmや/runなど)への書き込みである。
$ free -h
total used free shared buff/cache available
Mem: 5.8Gi 425Mi 5.3Gi 720Ki 156Mi 5.4Gi
$ mount -t tmpfs tmpfs /mnt
$ dd if=/dev/zero of=/mnt/bigfile bs=1M count=2048
$ free -h
total used free shared buff/cache available
Mem: 5.8Gi 2.4Gi 3.3Gi 2.0Gi 2.2Gi 3.4Gi
buff/cacheは2.2Giまで増えているが、availableは5.4Gi→3.4Giと2GiB減っている。
tmpfsの内容は書き戻す先のディスクを持たないため、カーネルはキャッシュとして解放できない。
buff/cacheには計上されるがavailableには加算されない。
見分けるにはshared列、または/proc/meminfoのShmemを見る。
$ grep -E "^(Shmem|Cached|MemAvailable):" /proc/meminfo
MemAvailable: 3550124 kB
Cached: 2241948 kB
Shmem: 2097772 kB
Cachedの大半がShmemで占められている場合、buff/cacheの見た目ほどメモリに余裕はない。
どのtmpfsが消費しているかはdf -hで確認できる。
$ df -h -t tmpfs
Filesystem Size Used Avail Use% Mounted on
tmpfs 64M 0 64M 0% /dev
tmpfs 2.9G 2.0G 908M 70% /mnt
キャッシュの解放を試すとき
キャッシュが解放される様子を確認したい場合はdrop_cachesを使う。
$ free -h
total used free shared buff/cache available
Mem: 5.8Gi 456Mi 2.2Gi 764Ki 3.3Gi 5.3Gi
$ sync
$ echo 3 > /proc/sys/vm/drop_caches
$ free -h
total used free shared buff/cache available
Mem: 5.8Gi 407Mi 5.4Gi 764Ki 156Mi 5.4Gi
buff/cacheが3.3Gi→156Miまで落ち、freeが5.4Giに戻る。
一方でavailableは5.3Gi→5.4Giとほぼ変わらない。availableの時点でキャッシュを解放できる前提の値になっているためである。
drop_cachesはキャッシュを捨てるだけでメモリ不足の解決にはならず、直後はディスクI/Oが増えて性能が落ちる。
検証用途に限定し、本番環境では使わない。
