malloc() と free() はどのように機能しますか?

OK、malloc に関するいくつかの回答は既に投稿されています。

さらに興味深いのは、無料の仕組みです。 (この方向では、malloc もよりよく理解できます)。

多くの malloc/free 実装では、free は通常、メモリをオペレーティング システムに返しません (または、少なくともまれなケースに限られます)。その理由は、ヒープにギャップが生じるため、2 GB または 4 GB の仮想メモリをギャップで締めくくってしまう可能性があるためです。仮想メモリが終了するとすぐに、非常に大きな問題が発生するため、これは避ける必要があります。もう 1 つの理由は、OS が特定のサイズと配置のメモリ チャンクしか処理できないことです。具体的には:通常、OS は仮想メモリ マネージャーが処理できるブロックのみを処理できます (ほとんどの場合、4KB などの 512 バイトの倍数)。

そのため、OS に 40 バイトを返すことはできません。では、無料は何をするのでしょうか?

Free は、メモリ ブロックを独自の空きブロック リストに入れます。通常、アドレス空間内の隣接するブロックを融合しようとします。フリー ブロック リストは、最初に管理データを含むメモリ チャンクの単なる循環リストです。これは、標準の malloc/free で非常に小さなメモリ要素を管理することが効率的でない理由でもあります。すべてのメモリ チャンクには追加のデータが必要であり、サイズが小さいほど断片化が発生します。

free-list は、新しいメモリ チャンクが必要なときに malloc が最初に確認する場所でもあります。 OS から新しいメモリを要求する前にスキャンされます。必要なメモリよりも大きなチャンクが見つかった場合、チャンクは 2 つの部分に分割されます。 1 つは発信者に返され、もう 1 つは空きリストに戻されます。

この標準的な動作には、さまざまな最適化があります (たとえば、メモリの小さなチャンクの場合)。しかし、malloc と free は非常に普遍的なものでなければならないため、代替手段が使用できない場合は、標準の動作が常にフォールバックになります。フリーリストの処理にも最適化があります。たとえば、サイズでソートされたリストにチャンクを格納します。ただし、すべての最適化には独自の制限もあります。

コードがクラッシュする理由:

その理由は、9 文字 (末尾のヌルバイトを忘れないでください) を 4 文字のサイズの領域に書き込むことによって、データのチャンクの「背後」にある別のメモリチャンクに格納されている管理データを上書きする可能性があるためです (ほとんどの場合、このデータはメモリ チャンクの「前」に格納されるためです)。その後、free がチャンクをフリー リストに入れようとすると、この管理データに触れて、上書きされたポインターに遭遇する可能性があります。これにより、システムがクラッシュします。

これはかなり優雅な振る舞いです。どこかのランナウェイ ポインタがメモリ フリー リストのデータを上書きし、システムがすぐにはクラッシュせず、後でいくつかのサブルーチンがクラッシュする状況も見てきました。中程度の複雑さのシステムであっても、このような問題をデバッグするのは非常に困難です。私が関与した 1 つのケースでは、メモリ ダンプで示された場所とはまったく異なる場所にあったため、クラッシュの原因を特定するのに数日かかりました。時限爆弾のようなものです。次の「free」または「malloc」はクラッシュしますが、その理由はわかりません!

これらは C/C++ の最悪の問題の 1 つであり、ポインターが非常に問題になる理由の 1 つです。


aluser がこのフォーラム スレッドで述べているように:

malloc() はシステム/コンパイラに依存するため、特定の答えを出すのは困難です。ただし、基本的には割り当てられたメモリを追跡し、その方法に応じて free の呼び出しが失敗または成功する可能性があります。

malloc() and free() don't work the same way on every O/S.


malloc/free の 1 つの実装は、次のことを行います:

<オール>
  • sbrk() を介して OS からメモリ ブロックを取得します (Unix 呼び出し)。
  • そのメモリ ブロックの周りにヘッダーとフッターを作成し、サイズ、権限、次のブロックと前のブロックの場所などの情報を追加します。
  • malloc が呼び出されると、適切なサイズのブロックを指すリストが参照されます。
  • その後、このブロックが返され、それに応じてヘッダーとフッターが更新されます。