← システム性能分析のしくみ コース
7. メモリ回収 — kswapdと直接回収でレイテンシが変わる
同じ「メモリが足りない」でも、裏で回収されるか自分で回収させられるかで応答時間がまったく変わります。その分かれ目を図で追っていきましょう。
① ゾーンの空きページには閾値が引かれている
空きページ 多いfree > high
平常運転割り当てが進み空きが減っていく
high watermarkここまで戻れば kswapd は眠りに戻る
さらに減る
low watermark下回ると kswapd を起こす
それでも減り続ける
min watermark下回ると割り当て側が自分で回収する
- ・カーネル文書が明記しているのは low で kswapd を起こし、min で直接回収に入る、という2つの閾値です
- ・min の値は /proc/sys/vm/min_free_kbytes から各ゾーンの WMARK_MIN として計算されます
- ・/proc/sys/vm/watermark_scale_factor は「kswapd をいつ起こし、どこまで空けたら眠らせるか」を決めます
- ・現在の空き量と各ゾーンの watermark は /proc/zoneinfo で確認できます
watermark は「空きページの残量に引かれた線」です。どの線を割ったかで、次に何が起きるかが変わります。
② low を割ると kswapd が裏で回収する(要求元は待たない)
プロセスがページを要求ページアロケータを呼ぶ
free が low を下回っていた
kswapd を起こすカーネルスレッド
ノードごとに常駐要求元はページを受け取るまだ min より上なので在庫がある
以降は非同期に進む
kswapd がページをスキャンして回収free が high に戻るまで続ける
プロセスは処理を続行待ち時間ゼロ
- ・kswapd は非同期に動くため、この経路では割り当て要求は止まりません
- ・カーネル文書は kswapd が眠りに戻る条件を watermark_scale_factor の説明として記述しています
low と min の間は「在庫を切らさないよう裏で補充している」状態です。ここで踏みとどまれている限り、遅延として表には出ません。
③ min を割るとプロセス自身が回収する(直接回収)
プロセスがページを要求kswapd の補充が消費に追いつかなかった
free が min watermark を下回っている
アロケータが遅い経路に入るすぐには渡せる在庫がない
要求元のコンテキストのまま回収に入る
そのプロセスがスキャンして回収direct reclaim
自分の時間を使う必要枚数を確保できたら
ようやくページを受け取り再開ここまでが丸ごと待ち時間
- ・カーネル文書は「必要なページが回収されるまで割り当てが停止する(stalled)」と説明しています
- ・回収してもなお足りない場合の最後の手段が OOM killer です
直接回収では、回収作業のコストがそのまま要求したプロセスの実行時間に乗ります。kswapd との決定的な違いはここです。
④ なぜ「突発的に数百ミリ秒遅い」が起きるのか
回収候補ページを探してスキャンLRUリストをたどるCPU時間
きれいなファイルページなら破棄で済む
破棄するだけディスクI/Oなし・比較的速い
だが汚れたページ・匿名ページに当たると
書き戻し or スワップアウトストレージI/Oの完了を待つ
ここが桁違いに遅い必要枚数に届くまで繰り返す
1回の割り当てが大きく膨らむCPUは空いているのに遅い
- ・CPU使用率もロードアベレージも正常なのにレイテンシだけ跳ねる、という形で観測されがちです
- ・スワップ先やファイルシステムのデバイスが遅いほど、直接回収の待ちは長くなります
- ・どちらを多く狙うかの傾向は /proc/sys/vm/swappiness(既定値 60・範囲 0〜200)が左右します
遅さの正体は「スキャンのCPU時間」ではなく、その途中で踏むストレージI/Oの完了待ちです。だから突発的で、値も大きくばらつきます。
⑤ 直接回収に落ちているかを /proc/vmstat で見分ける
cat /proc/vmstat名前と値の組が1行ずつ並ぶ
スキャン枚数がどちら由来か
pgscan_kswapdkswapd が裏でスキャンした枚数
pgscan_direct直接回収でスキャンした枚数
増えていたら要注意停止回数そのものを見る
allocstall_normal など直接回収で割り当てが止まった回数
ゾーン名が付くpgscan_direct_throttle回収を絞られている間にスキャンした枚数
- ・現在のカーネルでは pgscan_kswapd と pgscan_direct にゾーン名は付きません
- ・逆に allocstall にはゾーン名が付き、allocstall_normal や allocstall_dma32 として現れます
- ・古いカーネルでは pgscan_kswapd_normal のようにゾーン名が付いていたため、資料によって表記が異なります
- ・存在するフィールド名はカーネルのバージョンと設定によって変わるので、まず実機の出力を確認してください
- ・kswapd_low_wmark_hit_quickly は low watermark をすぐ割り直した回数で、補充が追いついていない兆候になります
見るべきは絶対値ではなく増分です。負荷時に pgscan_direct と allocstall 系だけが伸びていれば、レイテンシ悪化の原因は直接回収だと切り分けられます。
⑥ 対処は「min を割らせない」方向に効く
pgscan_direct が伸びているkswapd の補充が消費に負けている
kswapd が働き始める余裕を早める
watermark_scale_factor を上げる既定 10・最大 3000
kswapd の積極性が上がるmin_free_kbytes を見直すWMARK_MIN の元になる値
1024KB 未満は非推奨それでも足りないなら
そもそもの需要を減らす常駐量の削減・ノード増設
- ・カーネル文書は watermark_scale_factor を「kswapd の積極性を制御する」ものと説明しています
- ・単位は1万分率で、既定値 10 は利用可能メモリの0.1%にあたります
- ・min_free_kbytes を 1024KB より小さくすると高負荷時にデッドロックしやすくなる、と明記されています
- ・逆に大きくしすぎると即座に OOM を招く恐れがある、とも書かれています
どれも「kswapd に早めに動き始めさせて min まで落ちる前に補充させる」という同じ狙いです。直接回収そのものを速くする手段はありません。