OSのメモリ管理はどう動いている?仮想メモリとページングの仕組み


仮想メモリと物理メモリのマッピング 仮想メモリ ページ0 ページ1 ページ2 ページ3 ページテーブル 物理メモリ (RAM) フレーム0: ページ1 フレーム1: 空き フレーム2: ページ0 フレーム3: ページ3 ディスク (スワップ) ページ2 ページフォルト → スワップイン
仮想メモリから物理メモリ・ディスクへのマッピングのイメージ
ひよこ ひよこ

パソコンの「メモリ」ってよく聞くけど、なんで足りなくなったりするの?

ペンギン先生 ペンギン先生

いい質問だね。メモリっていうのは物理的なRAMのことで、CPUが今まさに使うデータを置いておく高速な作業台みたいなものだよ。でも容量には限りがあるから、たくさんのアプリを同時に開くと作業台がいっぱいになっちゃうんだ。

ひよこ ひよこ

じゃあメモリが足りなくなったらどうなるの?アプリが動かなくなっちゃう?

ペンギン先生 ペンギン先生

そこで登場するのが「仮想メモリ」という仕組みだよ。OSは各プロセスに専用のメモリ空間を与えるんだけど、実はこれ、物理メモリとは別の「仮想的なアドレス」なんだ。各プロセスは自分だけが広大なメモリを持っているように見えるけど、裏ではOSが物理メモリをやりくりしているんだよ。

ひよこ ひよこ

仮想と物理って、どうやって対応づけてるの?

ペンギン先生 ペンギン先生

「ページテーブル」という変換表を使うんだ。仮想メモリも物理メモリも「ページ」という4KBの小さなブロックに分割されていて、仮想ページ番号から物理ページ番号への対応がページテーブルに記録されているよ。CPUがメモリにアクセスするたびに、この変換表を引いて実際の物理アドレスを求めるんだ。

ひよこ ひよこ

毎回変換表を引くのって遅くないの?

ペンギン先生 ペンギン先生

すごく鋭いね!実はそのままだと遅すぎるから、CPUの中に「TLB(Translation Lookaside Buffer)」という専用キャッシュがあるんだ。最近使ったページの変換結果を覚えておいて、次に同じページにアクセスするときは変換表を引かずに一瞬で物理アドレスが分かる。TLBのヒット率は通常99%以上で、これがないとコンピュータは何倍も遅くなるよ。

ひよこ ひよこ

もし必要なページが物理メモリに載ってなかったらどうなるの?

ペンギン先生 ペンギン先生

それが「ページフォルト」だよ。プロセスがアクセスしたページが物理メモリにない場合、CPU割り込みを発生させてOSに処理を任せるんだ。OSはディスクからそのページを読み込んで物理メモリに配置し、ページテーブルを更新してからプロセスの実行を再開する。これを「デマンドページング」と呼んで、実際に必要になるまでメモリに読み込まないことで効率化しているんだよ。

ひよこ ひよこ

ディスクから読むってことは、物理メモリがいっぱいになったら誰かを追い出すの?

ペンギン先生 ペンギン先生

そのとおり!物理メモリが足りなくなると、しばらく使われていないページをディスク上の「スワップ領域」に書き出すんだ。これが「スワッピング」だね。パソコンが突然遅くなるのは、このスワップが頻繁に発生して、高速なRAMの代わりに低速なディスクを使っている状態が多いよ。SSDでもRAMの数十倍は遅いからね。

ひよこ ひよこ

アプリが「malloc」でメモリを確保するときって、裏で何が起きてるの?

ペンギン先生 ペンギン先生

mallocはまず、プロセス内のメモリプール(ヒープ領域)から空きブロックを探すよ。足りなければOSにページの追加を要求する。ここで厄介なのが「フラグメンテーション(断片化)」で、小さな空きメモリが飛び飛びに存在して、合計では十分なのに連続した大きなブロックが確保できない状態になることがあるんだ。メモリプールやスラブアロケータといった工夫で断片化を減らしているよ。

ひよこ ひよこ

もし本当にメモリが足りなくなって、スワップも限界になったらどうなるの?

ペンギン先生 ペンギン先生

ここからちょっと怖い話だよ。Linuxには「OOM Killer」という仕組みがあって、システム全体のメモリが枯渇すると、プロセスにスコアを付けて一番メモリを食っているプロセスを強制終了するんだ。スコアは /proc/[pid]/oom_score で見られて、メモリ使用量が多いほど高くなる。重要なプロセスは oom_score_adj で保護できるけど、何も設定しないと大事なプロセスが突然殺されることもあるよ。

ひよこ ひよこ

えっ、勝手に終了されちゃうの!?実際にそれで困ったことってあるの?

ペンギン先生 ペンギン先生

実はLinuxにはメモリの「オーバーコミット」という仕組みがあって、実際の物理メモリ+スワップ以上のメモリ確保を許可するんだ。mallocが成功しても、実際に使うときにメモリが足りなければOOM Killerが発動する。Kubernetesの現場では、Podのメモリ制限を超えるとcgroupのOOM Killerに殺されて「OOMKilled」ステータスで再起動を繰り返す、なんてインシデントが日常茶飯事だよ。メモリのrequestsとlimitsを適切に設定するのが本当に大事なんだ。

ひよこ ひよこ

メモリ管理って奥が深いんだね…。まずは何を覚えておけばいい?

ペンギン先生 ペンギン先生

まずは「仮想メモリのおかげで各プロセスが安全に独立して動ける」ということ、そして「物理メモリが足りなくなるとスワップが発生して遅くなる」という2点を押さえておけばOKだよ。サーバーを運用するなら、OOM Killerの存在とメモリ制限の設定は必須知識だね。