私は少し混乱しています。 OSコースでは、すべてのOSがページングまたはセグメンテーションによってメモリの断片化を処理し、連続した物理メモリの割り当てはまったくないと言われました。 OSは、連続したメモリ割り当てを回避するために、さまざまなレベルのアドレス指定(論理/物理)を使用します。さて ここ それについてはたくさんの議論があります。私の質問は次のとおりです。この問題は、論理アドレス指定をサポートするOSのC++プログラミングで実際に発生しますか(メモリの断片化が原因でプロセスがクラッシュしますか)?はいの場合、そもそも各OSが連続したアドレス指定を回避しようとするのはなぜですか?
仮想プロセスのアドレス空間での断片化と物理メモリでの断片化の2つの層があります。
最新のアプリケーションを見ると、メモリがOSに解放されていないため、時間の経過とともにメモリ使用量がどのように増加するかがわかります。これは他の原因が原因であると言えますが、メモリの断片化(割り当てられたメモリチャンクの非連続的な場所など)がこれの主な理由です。つまり、メモリアロケータはメモリをOSに解放することを拒否します。
物理メモリの断片化に関心がある場合は、メモリがページに編成されている場合でも、物理的に隣接するメモリチャンクを割り当てる必要があります。たとえば、仮想メモリのオーバーヘッドを回避する必要がある場合は、大きなページ(Linuxでは「巨大なページ」)を使用することをお勧めします。 x86_64は、4KiB、2MiB、および1GiBページをサポートします。必要なサイズの連続した物理メモリがない場合、それらを使用することはできません。
OSで「カーネル」を意味する場合、プロセスアドレス空間で発生する断片化(ヒープ断片化)には役立ちません。 Cライブラリは断片化を回避しようとする必要がありますが、残念ながら、常にそうできるとは限りません。リンクされた 質問 を参照してください。
メモリアロケータは通常、少なくとも何かが割り当てられている場合、メモリの大きなチャンクを解放することはできません。これには、ページ内の仮想メモリ構成を利用する部分的な解決策があります。これは、LinuxおよびBSDでは MADV_FREE
および DiscardVirtualMemory
Windowsの場合。部分的にしか使用されていない大量のメモリがある場合、そのメモリの一部が不要になり、メモリ不足で元に戻すことができることをカーネルに通知できます。これは、メモリの割り当て解除に非常にコストがかかるため、メモリのプレッシャーの下でのみ遅延して実行されます。ただし、多くのメモリアロケータは、パフォーマンス上の理由からまだそれを使用していません。
だからあなたの質問への答え-それはあなたがあなたのプログラムの効率をどれだけ気にかけているかに依存します。標準のアロケータが彼らのために仕事をするだけなので、ほとんどのプログラムは気にしません。一部のプログラムは、標準のアロケーターがその仕事を効率的に実行できない場合に問題が発生する可能性があります。
OSは連続したメモリ割り当てを回避していません。トップレベルには、ハードウェアとソフトウェアがあります。ハードウェアのリソースは限られており、この場合は物理メモリです。リソースを共有し、ユーザープログラムがその共有を処理しないようにするために、仮想アドレス指定レイヤーが発明されました。隣接する仮想アドレス空間をまばらな物理領域にマッピングするだけです。つまり、0x10000仮想アドレスは、あるプロセスでは0x80000の物理アドレスを指し、別のプロセスでは0xf0000を指すことができます。
ページングとスワッピングとは、一部のページまたはアプリのメモリ全体をディスクに書き込み、ある時点で元に戻すことを意味します。その後、物理ページのマッピングが異なる可能性があります。
そのため、プログラムには常に連続した仮想アドレス空間が表示されますが、これは実際には物理ハードウェア空間で断片化されています。ところで、それは一定のブロックサイズで行われ、無駄や未使用のメモリホールはありません。
さて、new
/malloc
関数によって引き起こされる第2レベルの断片化は、さまざまなサイズのメモリを割り当てて削除するという事実に関連しています。これにより、仮想空間でヒープが断片化されます。関数は、無駄ができるだけ少ないことを確認します。
したがって、ジェネリックC++(またはその他の言語)プログラミングでは、メモリの断片化については気にしません。割り当てるすべてのチャンクは、仮想空間で連続していることが保証されています(必ずしも物理空間である必要はありません)。