どうしてメモリはスワップするのか!?

こんにちは。斎藤です。

最近、新しいスキー板が欲しいなと思っています。現在使っているOGASAKAの板は5年目に入り、メーカーからこれ以上はチューンナップ(メンテナンス)はできないよ、と言われてしまいました。もし、次に買うなら、スノーボーダーの人と一緒にパウダーに飛び込みやすいセミファットタイプが良いのかなと考えています。皆さんのオススメ、ぜひ教えてください。

さて、今日はLinux Kernel上でのメモリ管理、特にページ回収(Page Reclaim)とスワップに絞り、「スワップの理由」「ページを回収する仕組み」そして「スワップの様子を観察する」の3点に分けてお話しします。「スワップするのが気持ち悪い」と考えている方は少なくないと思いますし、私もそう考えていた時期がありました。しかし、それは本当に悪い事なのか、今回掘り下げて行きます。

※主な対象Kernelは2.6.32(Red Hat Enterprise Linux(RHEL)/CentOS 6で採用)です。

スワップの理由

なぜスワップするのか

まず、なぜメモリ...ページはスワップするのでしょうか。大きく2の原因が考えられます。

  1. 物理メモリの空きが不足している
  2. 物理メモリの空き領域が断片化している

1は、多くの方が認識している事項と思います。問題は2です。仮に、物理メモリの空き容量が足りていても、malloc()した際に連続した領域が確保できないと、その領域を確保できるまで物理メモリに存在するページをスワップします。

vm.swappiness = 0 ≠ スワップしない

そんな中、スワップしないように、"sysctl.conf"に"vm.swappiness = 0"を割り当てる方がいるはずです。これは、ざっくり言うと物理メモリ割当時にキャッシュを優先するのか、プログラムの実行に必要なメモリを確保する事を優先するのかのバランスを決める設定です。

ある時期まではスワップはしないかもしれません。しかし、空きメモリの不足・断片化や、使用しているページキャッシュが存在している状態だと、新しいページが割り当てる事ができず、Kernel(kswapd)はスワップを始めようとします。

従って、vm.swappiness = 0にしたからと言っても、メモリの空き領域が断片化していれば、スワップが発生する事があります。

なお、vm.swappinessはkernel 2.6.18(RHEL5)と2.6.32では動きが違います。2.6.32では各プロセスのページは「ファイル」「無名(Anonymous)」の2種類に分かれて管理されています。その中で、vm.swappiness = 100だと1:1の確率で物理メモリに残すよう管理が行われ、vm.swappiness = 0にすると無名ページが最優先になります。詳細は、"mm/vmscan.c"の"get_scan_ratio()"関数を参照してください。

ページを回収する仕組み

LRU (Least Recently Used アルゴリズム)

ところで、スワップの対象となるページはどのようにして決められているのでしょうか。Linuxでは、LRUが利用されています。名前の通り、最近もっとも使われていなかったものを対象としています。仕組みを、図1を用いて説明します。縦軸がページのアドレス、横軸が使用履歴をつける時間の流れを示しています。該当の時間にページが使用されると「○」使用されてなければ「×」としました。

LRU.png (図1)

LRUは、使用されたページの履歴を一定期間(確保されたビット数分)保存します。そして、Page Reclaimが発生した際に、物理メモリから回収するページを順位付けする表として利用します。図1の場合、ページ3が最優先で回収され、ページ0が最後まで回収されない事になります。

実際のKernelでは、より細かな制御が行われています。

Lumpy Reclaim

Lumpy Reclaimは、Kernel 2.6.23で採用されました。

物理メモリ上に連続した空きページが存在しないとき、LRUをもとに回収されるページが決まる事は、先節で理解いただけたかと思います。ここで問題になるのが、連続した空きページが確保されるまで実行される点です。全体から見たら空き容量は確保できているのにも関わらず、ページがどんどんスワップ領域に移ってしまうのです。これは、もったいないことです。

そこで、「ちょうど良い場所を狙い撃ちして空き領域の断片化を解消する」のが、Lumpy Reclaimです。図2をご覧ください。

Lumpy Reclaim.png (図2)

少々荒っぽい方法ではあります。一方で、LRUによって物理メモリ上から回収されずに済むページが増える事になり、結果としてスワップメモリが利用される頻度も減ります。実装の詳細は"mm/vmscan.c"を読んでみてください。

なお、RHEL7で採用予定の3.10系では、Lumpy Reclaimは無くなっています

更なる効率化へ

Kernel 2.6.35から、Memory Compactionという、メモリのデフラグを行う仕組みが採用されています。これを利用すると、物理メモリ上の空きページが断片化していても、スワップを発生させる事無く連続した空きページが確保できます。ただ、WindowsのHDDのデフラグを経験されている方ならご存知かと思いますが、とても重い処理ではあります。

スワップの使用状況を確認してみよう

プロセス毎に確認する

プロセス毎にスワップしている領域を調べるbashスクリプトがあり、私にて少し調整したものがあります。以下からダウンロードし、root権限で実行してください。

実行すると、次のような結果が表示されます。バイト数はKiB単位です。

$ sudo getswap.sh
PID=1 - Swap used: 100 - (init)
PID=2 - Swap used: 0 - (kthreadd)
PID=3 - Swap used: 0 - (migration/0)
[中略]
PID=16295 - Swap used: 56892 - (java)
PID=19990 - Swap used: 0 - (mysqld_safe)
PID=20577 - Swap used: 4 - (mysqld)

いくつか、スワップしているプロセスがあります。javaのプロセスはバックグラウンドで待機しているデーモンで、実はスワップしていてもあまり影響はありません。mysqldもスワップしていますが、全体から見たら小さなものです。

以上から、スワップは起きているものの、対象となったプロセスを鑑みると業務影響は小さいと考える事ができます。

スワップ回数の統計

システム全体で、ページがスワップアウト・インしている回数・バイト数について、確認する事ができます。あらかじめ、sysstatをインストールしてください。

$ sar -B
15時30分01秒  pgpgin/s pgpgout/s   fault/s  majflt/s  pgfree/s pgscank/s pgscand/s pgsteal/s    %vmeff
15時40分01秒      0.00    190.39   1673.19      0.00    783.54      0.00      0.00      0.00      0.00
15時50分02秒      0.00    183.18   1686.88      0.00    787.95      0.00      0.00      0.00      0.00
16時00分01秒      0.00    193.80   1673.62      0.00    784.22      0.00      0.00      0.00      0.00

結果を確認すると、ページアウト(物理メモリ→スワップ)は発生しているようですが、ページイン(スワップ→物理メモリ)は発生していないようです。従って、LRUによって利用頻度の小さいページがページアウトされており、頻度の高いものはページアウトされていない事から、業務影響は小さいと考える事ができます。

ページインも同時に多発している場合は、いわゆるスラッシングが発生しており、システム全体のパフォーマンス低下しています。その場合は、利用しているインスタンスのメモリ搭載量を増やしたり、プロセス毎のメモリ使用量を削減するチューニングを施すなどして、状況を改善させられるよう努めてください。

また、利用頻度の小さいページがページアウトする事で、物理メモリ上にプログラムに割り当てられるページが増えるばかりでなく、より利用頻度の高いキャッシュ・バッファへ割り当てる領域も増える事になります。従って、スワップが発生している=システム全体のパフォーマンスが低下するばかりではないと、捉える事もできます。

おわりに

ここまで、スワップについて、「スワップの理由」「ページを管理する仕組み」そして「スワップの様子を観察する」の3点に分けてお話ししました。本エントリを通じて、起きているスワップはシステム全体に問題がある事なのか、逆に大勢に影響が無い事なのか、切り分けられるようになったのではないでしょうか。実際は更に奥深い世界があるので、お時間が許す限り皆様もぜひ掘り下げてみてください。

また、LinuxのKernelのバージョンが上がるにつれて、メモリ管理もより賢くなっている事がおわかりいただけたかと思います。今回は解説しませんでしたが、メモリ管理以外も改善が進んでいます。業務で利用されているソフトウェアに対して充分な検証を踏まえる事が前提ですが、システム全体のパフォーマンス向上策の一つとして、Kernelやディストリビューションのバージョンアップは考慮に値します。

最後に、私は現職になってからじっくりとLinux Kernelのソースコードを読むようになりました。その中で、Linux Kernelの動きはさることながら、様々な知恵が注ぎ込まれている事に感銘を受けました。今後も、折に触れて読んで行こうと思っております。

それでは皆様、ごきげんよう。

【ハートビーツからのお知らせ】

ハートビーツは、有人で24時間 365日、お預かりしたサーバを監視・障害対応を行うMSP(Management Service Provider)です。

サーバのパフォーマンスが上がらないとお悩みの時は、ぜひ当社の「フルマネージドサービス」の導入をご検討ください。

参考文献

株式会社ハートビーツのインフラエンジニアから、ちょっとした情報をお届けします。