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
sharedtmpfsや共有メモリの使用量Shmem
buff/cacheディスクキャッシュとカーネルの回収可能な領域Buffers + Cached + SReclaimable
available新しくプロセスを起動するときに使える見込みのメモリMemAvailable

usedtotal - 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のファイルを書き込んでfreeavailableの動きを比較する。

$ 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/meminfoMemAvailableとして計算した見積り値で、以下のように求められる。

  • MemFreeから、システムが動作を維持するために確保しておく分(low watermark)を引く
  • ファイルキャッシュを加算する。ただしキャッシュの半分程度は解放できないものとみなして差し引く
  • 回収可能なスラブ(SReclaimable)を同様に半分程度差し引いて加算する

キャッシュを保守的に見積もっているため、availablefree + 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/meminfoShmemを見る。

$ 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が増えて性能が落ちる。 検証用途に限定し、本番環境では使わない。

参考