仮想メモリの仕組み——ページとキャッシュ
動いているプロセスはそれぞれ、自分専用の広大なメモリ空間を見ているつもりでいますが、これは仮想アドレスというカーネルが見せている錯覚です。実際のメモリは4KiB程度のページという単位で物理メモリに対応付けられ、必要になった瞬間に割り当てるページフォルトという遅延戦略で無駄なく使われます。free コマンドが示す buff/cache は使用中に見えて実質は空きの仲間であり、この誤読は多くの初心者がつまずく落とし穴です。/proc/meminfo の主要な行まで読めるようになれば、メモリ不足の判断を数字で行えるようになります。
動いているプロセスはそれぞれ、自分専用の広大なメモリ空間を丸ごと使えているつもりでいる。だがこれは錯覚で、実際にはカーネルが用意した仮想アドレス空間を見ているにすぎない。
この仕組みのおかげで、複数のプロセスが同じような番地を使っていても互いに干渉しないし、あるプロセスが暴走して変な番地へ書き込もうとしても、他のプロセスの領域を壊さずに済む。
🗺️ 仮想アドレス空間という個室
各プロセスは起動すると、0番地から始まるかのような自分専用のアドレス空間を割り当てられる。この変換作業を担うのがMMU(メモリ管理ユニット)というCPU内の仕組みで、カーネルが管理する変換表を参照しながら、仮想アドレスを実際の物理アドレスへ都度読み替えている。
この変換のおかげで、プログラムを書く側は「メモリは自分専用に広く使える」という単純な前提でコードを書ける。裏側の込み入った調整はカーネルに任せられる。
🧩 ページという単位
仮想アドレスと物理アドレスの対応付けは、1バイトずつ細かく行うわけではない。ページと呼ばれる一定サイズ(多くの環境で4KiB)の塊を単位として、まとめて管理される。
⏳ ページフォルトと遅延割り当て
プロセスがメモリを要求した瞬間に、カーネルがすぐ物理メモリを割り当てるとは限らない。実際に読み書きが発生するまで割り当てを先延ばしにするデマンドページングという戦略が広く使われている。
プロセスが「まだ物理メモリと対応付けられていないページ」へアクセスすると、CPUはページフォルトという例外を発生させる。カーネルはこれを検知し、そこで初めて物理メモリを割り当てて対応付ける。
この遅延戦略のおかげで、プログラムが「確保しただけで実際には使わない」メモリ領域に対して、無駄に物理メモリを消費せずに済む。使う分だけ、使うタイミングで実体が割り当てられる。
💾 free コマンドで見るメモリ
実際にメモリの使用状況を見るには free コマンドを使う。ただし出力の読み方には落とし穴があり、buff/cache の列を「使用中で危険な状態」と誤解する初心者は多い。
🔄 スワップという逃がし先
物理メモリが足りなくなってきたとき、カーネルは使用頻度の低いページを一時的にディスク上のスワップ領域へ退避させ、物理メモリを他の用途へ回す。これがスワップだ。
📄 /proc/meminfo で詳しく見る
free の集計元をさらに細かく見たいときは /proc/meminfo を直接読む。free コマンドが表示する各値は、実はこのファイルの数字を要約したものにすぎない。
MemTotal は物理メモリの総量、MemFree は本当に何も使っていない量、MemAvailable は buff/cache の再利用可能分まで含めた実質的な空き量にあたる。Buffers はブロックデバイスの入出力用、Cached はファイル内容のキャッシュで、どちらも free コマンドの buff/cache に合算されている。
仮想メモリという抽象化は、プロセスを互いに守りながら、限られた物理メモリを使い回すための土台になっている。次はこの土台の上に、プロセスの世界そのものを分けたり絞ったりする namespace と cgroups を見ていく。