freeはどのようにして解放する量を知っていますか?


385

Cプログラミングでは、解放する引数として任意の種類のポインタを渡すことができますが、解放するために割り当てられたメモリのサイズはどのようにしてわかりますか?いくつかの関数へのポインターを渡すときはいつでも、サイズも渡す必要があります(つまり、10要素の配列は、配列のサイズを知るためにパラメーターとして10を受け取る必要があります)。ただし、サイズを渡す必要はありません。無料機能。なぜそうしないのか、そして同じ長さの関数を自分の関数で使用して、配列の長さの追加の変数をあちこち移動する必要がないようにすることができますか?


同様の質問:stackoverflow.com/questions/851958/…(完全に重複しているわけではないと思いますが)
ジョンカーター、

バディシステムは、各ブロック内のオーバーヘッドなしに、ポインタに基づいて把握することができ、それを行うための別の方法です。
EvilTeach 2014年

この投稿は、それをうまく説明:stackoverflow.com/questions/1957099/...を
Zeeshanマフムード

回答:


349

を呼び出すときにmalloc()、割り当てるメモリの量を指定します。実際に使用されるメモリの量はこれよりわずかに多く、ブロックの大きさを(少なくとも)記録する追加情報が含まれます。他の情報に(確実に)アクセスすることはできません。

を呼び出すとfree()、追加情報を調べてブロックの大きさを確認します。


44
参考までに、たとえばBSDはmalloc_size()malloc()edポインタからブロックサイズに確実にアクセスする必要があります。しかし、信頼できるポータブルな方法はありません。
laalto 2009年

50
この追加の情報ブロックは、返されたポインターの前にあるということは重要だと思います。
GeorgSchölly09年

39
@gsまあそれは実装依存です。しかし、はい、それは通常そこにあります。
Falaina、2009年

31
free()プログラマーがmalloc()ブロックの大きさを正確に報告する必要がある場合の恐怖を想像できますか?メモリリークは、それなりにひどいです。
MusiGenesis

35
なぜその情報が利用可能ですmalloc()free()していますが、配列のサイズを保存する必要がありますか?なぜblockSize(ptr)とにかく情報を保存しているようなことを可能にしないのですか?
corsiKa 2014年

144

Cメモリ割り当て関数のほとんどの実装は、インラインまたは個別に、各ブロックのアカウンティング情報を格納します。

典型的な方法の1つ(インライン)は、要求されたヘッダーとメモリの両方を実際に割り当て、最小サイズにパディングします。たとえば、20バイトを要求した場合、システムは48バイトのブロックを割り当てることがあります。

  • サイズ、特殊マーカー、チェックサム、次/前のブロックへのポインターなどを含む16バイトのヘッダー。
  • 32バイトのデータ領域(20バイトは16の倍数に埋め込まれます)。

次に与えられるアドレスは、データ領域のアドレスです。次に、ブロックを解放freeすると、指定したアドレスを取得し、そのアドレスまたはその周りのメモリを詰めていないことを前提として、その直前のアカウンティング情報を確認します。グラフィカルに、それは次のようになります。

 ____ The allocated block ____
/                             \
+--------+--------------------+
| Header | Your data area ... |
+--------+--------------------+
          ^
          |
          +-- The address you are given

ヘッダーとパディングのサイズは完全に実装によって定義されることに注意してください(実際には、全体が実装によって定義されます(a)が、インラインアカウンティングオプションは一般的なものです)。

アカウンティング情報に存在するチェックサムと特別なマーカーは、「メモリアリーナの破損」や「2回解放」などのエラーの原因となることがよくあります。

パディング(割り当てをより効率的にするため)は、要求されたスペースの終わりを少し超えて問題を引き起こすことなく書き込むことができる理由です(ただし、それを行わないでください。これは未定義の動作であり、場合によっては機能するため、それはそれを行うことは大丈夫だという意味です)。


(a)malloc組み込みシステムでの実装を記述しましたが、128バイト以下(システムで最大の構造のサイズ)を要求したとしても、128バイトを要求したとしても、128バイトが得られます(それ以上要求すると、 NULLの戻り値で満たされる)。非常に単純なビットマスク(つまり、インラインではない)を使用して、128バイトのチャンクを割り当てるかどうかを決定しました。

私が開発した他のものには、16バイトチャンク、64バイトチャンク、256バイトチャンク、および1Kチャンク用の異なるプールがあり、ビットマスクを使用して、どのブロックが使用または利用可能かを決定しました。

これらの両方のオプションは、会計情報のオーバーヘッドを削減するとの速度増加するために管理mallocし、free私たちが働いていた環境で特に重要なのは、(解放する際に隣接するブロックを合体する必要はありませんが)。


@paxdiabloこれは、mallocが連続したメモリブロックを割り当てないことを意味しますか?
user10678 2017年

2
@ user10678の唯一の実際の要件はmalloc、成功した場合に、少なくとも要求したものと同じ大きさのメモリブロックを提供することです。個々のブロックは、ブロック内の要素へのアクセス方法の点で隣接していますが、ブロックの元の領域が隣接している必要はありません。
paxdiablo 2017年

関連質問:なぜmalloc / freeのバリエーションがなく、解放時にサイズを指定するので、サイズを格納する必要がないのですか?
user253751

@ user253751、その後のtheer 1つのので、より多くの事は、あなたが以上のポインタ自体上記のトラックを保持する必要があります。それは、不必要な両方だ危険:void *x = malloc(200); free(x, 500);されていないだけでなく終わろう:-)いずれの場合も、効率化のため、実際のバッファのサイズは、(あなたはこのに頼ることができない)大きくてもよいです。
paxdiablo

@paxdiabloまた、サイズを保持するためのメモリの浪費も回避されます。
user253751

47

comp.lang.cFAQリストから:freeは、解放するバイト数をどのようにして知るのですか?

malloc / freeの実装は、割り当てられた各ブロックのサイズを記憶しているため、解放するときにサイズを思い出す必要はありません。(通常、サイズは割り当てられたブロックに隣接して保存されます。そのため、割り当てられたブロックの境界が少しでもオーバーステップすると、通常はうまく機能しなくなります)


2
これは答えではありません。問題はまさにこれです:なぜブロックのサイズを確実に解放できるのに、プログラマーが利用できる関数がないのですか?
バナナハ

これは確かにmalloc apiの実装の詳細であり、(私の知る限り)標準的な方法でこの情報を取得するapiはありません。「システム」はそれを記録し、それをで使用しfreeます。多分答えはあなたを満足させていないかもしれませんが、私はあなたがより一般的に適用可能な情報でそれを得るとは思いません:-)
jdehaan

6

この答えは、どのようにfree()が割り当て解除するメモリの量を知っているのかから再配置されていますか?明らかに重複する質問によって、私は突然答えることを妨げられました。この回答は、この重複に関連しているはずです。


の場合malloc、ヒープアロケータは、free後でメモリを使用するために必要な関連詳細への、元の返されたポインタのマッピングを格納します。通常、これには、使用中のアロケータに関連する任意の形式でメモリ領域のサイズを格納することが含まれます。

freeポインタの「名前を変更」したり、ポインタを複製したりしても失敗しません。ただし、参照はカウントされず、最初のものだけfreeが正しいでしょう。追加freeのは「二重解放」エラーです。

free以前mallocのによって返されたものとは異なる値を持つポインターを試みても、まだ解放されていない場合はエラーになります。から返されたメモリ領域を部分的に解放することはできませんmalloc


malloc呼び出しで返されるポインターの値を変更しました。そして私はそれをエラーなしで解放しました。どうして?ここを参照してください:stackoverflow.com/questions/42618390/...
smwikipedia

4

関連するメモでは、GLibライブラリには暗黙のサイズを保存しないメモリ割り当て関数があり、サイズパラメータを解放するだけです。これにより、オーバーヘッドの一部を排除できます。


3

malloc()またfree()、システム/コンパイラに依存するため、具体的な答えを出すのは困難です。

この他の質問の詳細情報。


2
それらは実際にはライブラリに依存しています(通常はCライブラリであり、通常はOSに非常に密接にリンクされています)。コンパイラにとって、これらは単なる関数です。
ドナルフェロー

2

を呼び出しmallocたときに、ヒープマネージャは割り当てられたブロックに属するメモリの量をどこかに保存しました。

自分で実装したことはありませんが、割り当てられたブロックの直前のメモリにはメタ情報が含まれている可能性があります。


3
これは可能な実装の1つですが、すべてのメモリが完全に異なるページの1つのテーブルで追跡され、必ずしも割り当て元のメモリプールに近い場所ではないシステムを考案できます。
ephemient

2

元々の手法は、少し大きいブロックを割り当て、最初にサイズを保存して、アプリケーションにブログの残りの部分を提供することでした。余分なスペースはサイズを保持し、再利用のために空きブロックをスレッド化するためにリンクする可能性があります。

ただし、これらのトリックには特定の問題があります。たとえば、キャッシュとメモリ管理の動作が不十分です。ブロック内でメモリを使用すると、ページが不必要にページングされる傾向があり、またダーティページが作成されて、共有とコピーオンライトが複雑になります。

したがって、より高度な手法は、個別のディレクトリを保持することです。メモリの領域が同じ2のべき乗サイズを使用するエキゾチックなアプローチも開発されました。

一般的に、答えは次のとおりです。状態を維持するために、別のデータ構造が割り当てられます。


1

質問の後半に答えるには、はい、できます。Cでかなり一般的なパターンは次のとおりです。

typedef struct {
    size_t numElements
    int elements[1]; /* but enough space malloced for numElements at runtime */
} IntArray_t;

#define SIZE 10
IntArray_t* myArray = malloc(sizeof(intArray_t) + SIZE * sizeof(int));
myArray->numElements = SIZE;

これは、BSD mallocが小さなオブジェクトに使用する手法とは完全に異なる手法です(Pascalスタイルの配列を作成するための完全に優れた手法です)
Pete Kirkham

0

mallocを呼び出すと、要件からより多くのバイトが消費されます。このより多くのバイト消費には、チェックサム、サイズ、その他の追加情報などの情報が含まれています。そのときにfreeを呼び出すと、アドレスが見つかる追加情報に直接移動し、空きブロックの量もわかります。


0

2番目の質問に答えるには、はい、(malloc() すべての)単純に、すべての配列内の最初のセルを配列のサイズに割り当てるのと同じ手法を使用できます。これにより、追加のサイズ引数を送信せずに配列を送信できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.