通常、2 つの異なるレイヤーが存在します。
1 つのレイヤーは、通常は C 標準ライブラリの一部として、アプリケーション レベルに存在します。これは、malloc
andのような関数を介して呼び出すものですfree
(またはoperator new
C++ では、通常は を呼び出しますmalloc
)。このレイヤーは割り当てを処理しますが、メモリやそれがどこから来るかは知りません。
OS レベルのもう一方のレイヤーは、ユーザーの割り当てを認識せず、気にしません。予約、割り当て、アクセスされた固定サイズのメモリ ページのリストと、マップ先などの各ページ情報のみを保持します。
どちらの層にも多くの異なる実装がありますが、一般的には次のように機能します:
メモリを割り当てるとき、アロケータ (「アプリケーション レベルの部分」) は、それが提供できる本のどこかに一致するブロックがあるかどうかを調べます (一部のアロケータは、必要に応じて大きなブロックを 2 つに分割します)。
適切なブロックが見つからない場合は、オペレーティング システムから新しいブロック (通常は要求したものよりもはるかに大きい) を予約します。sbrk
またはmmap
Linux、またはVirtualAlloc
Windows では、その効果のために使用される関数の典型的な例になります。これは、オペレーティング システムに意図を示し、いくつかのページ テーブル エントリを生成する
以外には、ほとんど何もしません。
次に、アロケータは (理論上は論理的に) 通常の動作モードに従ってその大きな領域を小さな断片に分割し、適切なブロックを見つけて、それをユーザーに返します。この返されたメモリは必ずしも物理メモリとして存在するとは限らないことに注意してください (ただし、ほとんどのアロケータは、割り当てられた各ユニットの最初の数バイトにメタデータを書き込むため、必ずページを事前フォールトします)。
その間、バックグラウンド タスクは、あるプロセスで一度使用されたが解放されたメモリ ページを目に見えないようにゼロにします。遅かれ早かれ誰かがメモリを要求するため、これは一時的なベースで常に発生します (多くの場合、それはアイドル タスクが行うことです)。
割り当てられたブロックを含むページのアドレスに初めてアクセスすると、エラーが発生します。このまだ存在しないページ (物理的にではなく、論理的に存在する) のページ テーブル エントリは、ゼロ ページのプールからのページへの参照に置き換えられます。何も残っていないというまれなケースでは、たとえば、大量のメモリが常に割り当てられている場合、OS は、すぐにはアクセスされないと思われるページをスワップアウトし、ゼロにして、このページを返します。
これで、ページはワーキング セットの一部になり、実際の物理メモリに対応し、プロセスのクォータに加算されます。プロセスの実行中に、必要なメモリの量とアクセス方法に応じて、特定の制限を超えると、ページがワーキング セットに出入りしたり、ページ アウトされたりインされたりすることがあります。
を呼び出すfree
と、アロケータは解放された領域をブックに戻します。代わりに、メモリがもう必要ないことを OS に通知する場合がありますが、実際には必要ないため、通常は発生しません。少し余分なメモリを保持して再利用する方が効率的です。また、通常、割り当て/割り当て解除するユニットは、OS が動作するユニットと直接対応していないため、メモリを解放するのは簡単ではない場合があります (また、sbrk
正しい順序で発生する必要がある場合) )。
プロセスが終了すると、OS は単にすべてのページ テーブル エントリを破棄し、アイドル タスクがゼロにするページのリストにすべてのページを追加します。したがって、物理メモリは、次のプロセスが要求するために利用できるようになります。