windows 64ビットの大規模なmallocs




memory 64bit (8)

malloc()が特に64ビットで失敗する理由は何ですか?

私の具体的な問題は、64ビットシステム上で巨大な10GBのRAMチャンクをmallocしようとしていることです。 マシンは12GBのRAMと32GBのスワップを備えています。 はい、mallocは極端ですが、なぜ問題になるのでしょうか? これは、インテルとMSFTの両方のコンパイラを備えたWindows XP64にあります。 mallocは成功することがありますが、時にはそうではありません。約50%です。 8GBのmallocは常に動作しますが、20GBのmallocは常に失敗します。 mallocが失敗した場合、プロセスをやめて新たなプロセスを再開しない限り、繰り返し要求は機能しません(成功した場合は50%のショットを取得します)。 他の大きなアプリは動いていません。 新しくリブートした直後でも起こります。

32ビット(または31ビット)のアドレス空間を使い切ってしまった場合、要求に割り当てられるほどのアドレス範囲がないため、32ビットでmallocが失敗すると思います。

物理RAM ハードドライブのスワップ領域を使い切ってしまった場合、mallocが失敗することも想像できます。 これは私には当てはまりません。

しかし、なぜmallocが失敗するのでしょうか? 私は他の理由も考えられません。

とにかくメモリマップされたファイルで置き換えられると思われる私の具体例よりも、一般的なmalloc質問にもっと興味があります。 失敗したmalloc()は、あなたのツールを理解し、基本に驚かないことを望む他のものよりもパズルのほうがはるかです。


しかし、なぜmallocが失敗するのでしょうか? 私は他の理由を考えることができません

前に暗黙に述べたように、メモリの断片化


それはおそらく断片化です。 簡単にするために、例を使用しましょう。

メモリは1つの12kbモジュールで構成されています。 このメモリはMMUの1kbブロックに編成されています。 したがって、12 x 1kbのブロックがあります。 お使いのOSは100バイトを使用しますが、これは基本的にページテーブルを管理するコードです。 だから、あなたはそれを交換することはできません。 その後、あなたのアプリはそれぞれ100バイトずつ使用します。

さて、お使いのOSとアプリケーションを実行するだけで(200バイト)、すでに200バイトのメモリを使用しています(2kbブロックを占有)。 malloc()ために正確に10kbを残す。

今度は、 malloc()によっていくつかのバッファー - A(900バイト)、B(200バイト)から始めました。 それから、あなたはAを解放します。今、あなたは9.8キロバイトフリーです(連続していません)。 だから、あなたはmalloc() C(9kb)にしようとします。 突然、あなたは失敗します。

末尾に8.9k、フロントエンドに0.9kの連続があります。 Bが最初の1kブロックと2番目の1kブロックにまたがるため、最初のブロックを最後に再マップすることはできません。

あなたはまだ8キロバイトのブロックをmalloc()することができます。

確かに、この例は少し工夫されていますが、助けてくれることを願っています。



mallocは連続したメモリ範囲を割り当てようとしますが、これは最初はスワップメモリ​​がどのように機能するかによって(少なくとも私が覚えている限り)実際のメモリになります。 あなたのOSが時には連続した10GBのブロックを見つけられず、RAMに同時に実際のメモリを必要とするすべてのプロセスを同時に残すことができます(その時点であなたのmallocは失敗します)。

あなたは実際に10GBの連続したメモリを必要としますか?あるいは、いくつかの小さなブロックの周りにストレージクラス/構造体をラップして、代わりにチャンクであなたのメモリを使用することができますか? これにより、連続した巨大な要件が緩和され、プログラムで使用されていないチャンクにスワップファイルを使用できるようになります。


VirtualAlloc()VirtualFree()直接使ってみましたか? これは、問題を特定するのに役立ちます。

  • CランタイムヒープとNTヒープをバイパスします。
  • 仮想アドレス空間を予約してからコミットすることができます。 これは、どの操作が失敗するかを示します。

仮想アドレススペースの予約が失敗した場合( Sysinternals VMMapは、あなたが言ったことから判断してはいけませんが)、理由を説明するのに役立ちます。 「空き領域を表示」をオンにして、空き仮想アドレス空間が断片化されている様子を確認します。


私は興味深い質問を見つけたので、私はそれを理論的なPOVから研究しようとしました:

64ビット(実際にはチップの制限により48ビット、OSの制限により44ビット)では、仮想メモリの断片化、つまり連続した仮想アドレス空間の不足ではいけません 。 その理由は、あまりにも多くの仮想アドレス空間があり、それを使い果たすのは実際的ではないからです。

また、仮想メモリは割り当て要求を満たすために連続した物理メモリアドレス範囲を必要としないことを意味するため、 物理メモリの断片化は問題ではありません。 その代わりに、十分に大きなメモリページセットで満足することができます。

つまり、仮想メモリに適用されるいくつかの他の制限があります。

Windowsに確実に存在するもう1つの制限はコミット制限です。 これに関する詳細情報:

http://blogs.technet.com/b/markrussinovich/archive/2008/11/17/3155406.aspx

例えば、実際の実装が実際のハードウェアとどのように機能するかというような他の可能性のある制限が存在する可能性があります。 仮想アドレス空間と物理アドレス空間のマッピングを作成しようとすると、仮想アドレスマッピングを行うためにページテーブルのエントリが不足していると考えています... OSメモリアロケータコードはこのようなシナリオに対処しますか? おそらく...

ページテーブルが実際に仮想アドレス変換を行うために実際にどのように機能するかについては、ここで詳しく説明しています。

http://en.wikipedia.org/wiki/Memory_management_unit


問題は、64ビットアプリケーションをコンパイルするときにVisual StudioがWIN64を定義していないことです。通常は64ビットアプリケーションでは間違ってWIN32を保持します。 これにより、 _HEAP_MAXREQが定義されているときに実行時に32ビット値が使用されるため、すべての大きなmalloc()は失敗します。 あなたのプロジェクト(プロジェクトのプロパティ、前処理された定義の下で)をWIN64に変更した場合、非常に大きなmalloc()は何の問題もないはずです。


ここでは、ヒープの最大要求サイズがリンクされたCRTライブラリによって定義されていることを公式に示しています(整数オーバーフローが0になっているため、NULLを返さなかった理由はありません)(_HEAP_MAXREQ)。

http://msdn.microsoft.com/en-us/library/6ewkz86d.aspx

私の答えは大きなウィンドウの割り当てについてはhereをチェックしてください。私は、Vista / 2008メモリモデルの進歩に関するMS論文への参照を含みます。

要するに、在庫CRTはネイティブの64ビットプロセスであっても、4GB以上のヒープサイズをサポートしていません。 VirtualAlloc *やCreateFileMappingなどのアナログを使用する必要があります。

ああ、私はあなたの大規模な割り当てが実際に成功していると主張していることに気付きました。これは実際には間違っています。あなたはmalloc(0x200000000)を誤解しています。 (これは16進数で8GBです)、何が起こっているのは、テストハーネスのキャストや他の効果のために0バイトの割り当てを要求していることです.0xfffff000バイトのヒープがコミットされているよりも大きなものはほとんど見かけません。単純に整数のオーバーフローがキャストダウンしているのを見ているだけです。

ワイプへの言葉、またはヒープ・サンティを保存するためのヒント*

MALLOC(または他の動的要求)を使用してメモリを割り当てるだけの方法

void *foo = malloc(SIZE);

ダイナミックメモリ要求の価値は、要求の "()" PARENの中で決して(必ずしも厳しくすることはできません)計算されなければなりません

mytype *foo = (mytype *) malloc(sizeof(mytype) * 2);

危険は、 整数のオーバーフローが発生することです。

呼び出し時に算術演算を実行するのは常にコーディング・エラーです。要求を評価するステートメントの前に、要求されるデータの合計を常に計算しなければなりません

なぜそれはとても悪いですか? これが間違いであることはわかっています。動的リソースに対する要求があるため、将来このリソースを使用するポイントが必要です。

私たちが要求したものを使用するには、それがどれだけ大きいかを知る必要があります。 (例えば、配列数、型サイズなど)。

これは、資源要求の中で算術が全く見えなくても、そのデータを適切に使うためには、そのコードを再度複製しなければならないということです。





virtual-memory