2009-11-05 9 views
9

私のプログラムでメモリの断片化に問題があり、しばらくしてから非常に大きなメモリブロックを割り当てることができません。私はこのフォーラムで関連記事を読んでいます - 主にthisです。そして私はまだいくつかの質問があります。ヒープフラグメンテーションとウィンドウメモリマネージャ

メモリの画像を取得するためにメモリ領域profilerを使用しています。私はcin >> varを含む1行のプログラムを書いた。そしてメモリの写真を撮った:上部円弧上

alt text http://img22.imageshack.us/img22/6808/memoryk.gif - 緑色が割り当てられた黄色、空のスペースを示し、赤色はコミット。私の質問は、右側に割り当てられたメモリは何ですか?メインスレッドのスタックですか?このメモリは解放されず、必要な連続メモリが分割されます。この単純な1行のプログラムでは、スプリットはそれほど悪くはありません。私の実際のプログラムでは、アドレス空間の真ん中にもっと多くのものが割り当てられています。どこから来ているのかわかりません。私はまだそのメモリを割り当てていません。

  1. どうすればこの問題を解決できますか?私はnedmallocやdlmallocのようなものに切り替えることを考えていました。しかし、それは私が明示的に割り当てたオブジェクトにのみ適用されますが、図に示された分割は消えないでしょうか?または、CRT割り当てを別のメモリマネージャに置き換える方法はありますか?

  2. オブジェクトといえば、nedmallocのC++用のラッパーがありますので、オブジェクトを割り当てるためにnewとdeleteを使用できますか?

ありがとう。

+1

Microsoft Security Essentialsは、元の質問にリンクされている「プロファイラ」アプリケーションにWin32.Bisar!rtsトロイの木馬が含まれていると考えています。 –

答えて

12

まず、私のツールを使用していただきありがとうございます。私はあなたがそれが有用であると感じ、機能要求や貢献を自由に提出してくれることを願っています。

通常、アドレス空間内の固定小数点にある薄いスライスは、リンクされたdllが優先アドレスでロードされているために発生します。アドレス空間で上位にロードされるものは、MicrosoftオペレーティングシステムのDLLです。オペレーティングシステムでは、dllの読み取り専用部分をすべてプロセス間で共有できるため、これらのすべてを優先アドレスでロードできると、オペレーティングシステムにとってより効率的です。

あなたが見ることができるスライスは、何も心配することはありません、あなたのアドレス空間から何もほとんど切り取っていません。あなたが指摘したように、アドレス空間の他の場所にロードするdllもあります。 IIRC shlwapi.dllは、特に悪い例です。約0x2000000(やはりIIRC)でロードされます。これは、多くの場合、利用可能なアドレス空間の大部分を2つの小さな部分に分割します。この問題は、いったんDLLがロードされると、このスペースを割り当てるために何もできないことになります。

DLLと(直接または別のDLLを介して)リンクする場合は、何もできません。 LoadLibraryを使用すると、卑劣になり、その優先アドレスを予約し、予約されたメモリを解放する前に、頻繁にアドレス空間のどこかでそれを再配置することができます。しかし、これは必ずしもうまくいかない。

アドレス空間モニタはVirtualQueryExを使用してプロセスのアドレス空間を調べますが、psapiライブラリから別のツール(たとえばProcess Explorer)を使用して、どのファイル(DLLを含む)がマップされているかを表示できますアドレス空間のどの部分に挿入するかを指定します。

あなたが見つけたように、2GBのユーザーアドレス空間で部屋を使い果たすのは簡単です。基本的に、メモリの断片化を防ぐには、連続した大きなメモリブロックを必要としない方がよいでしょう。レトロフィットは困難ですが、アプリケーションを「中規模サイズ」のチャンクで動作するように設計すると、通常、アドレス空間の使用効率が大幅に向上します。

同様に、メモリマップファイルまたはAddress Windowing Extensionsを使用して、ページング戦略を使用することができます。

+0

すばらしいツールをご利用いただき、ありがとうございました。Process Explorerの機能を指しています。 – Budric

+0

@Charles Bailey:Re静的リンク - DLLをリベースできませんでしたか? – rpg

+0

はい、あなたはDLLをリベースすることができますが、*これはアドレス空間の断片化とロード時間を最適化するのに役立ちます...通常はあなたが所有するDLLでこれを行うべきであり、あまりにも多くのマイクロコンパイルを行うと、ある時点で1つの特定のexeでうまく動作するDLLのセットですが、他のexeでは最適化されたローディング戦略があまり得られません。 DLLが再構築され、サイズが変更された場合は、プロセスを再実行する必要があります。だから、それはある程度は機能しますが、それに頼らなければならない場合、メンテナンスの悪循環に陥る可能性があります。 –

1

実行ファイルですか?それはどこかのアドレス空間にロードされなければなりません....

2に関しては、グローバルな新機能と削除機能をオーバーライドするのはかなり簡単です...それらを定義するだけです。

2

さまざまなサイズのオブジェクトを頻繁に割り当てたり割り当てを解除したりして、メモリの断片化の問題が発生することが想定されます。

これらを回避するにはさまざまな方法があります。あなたが言及した異なるメモリマネージャは、あなたの断片化の問題を解決することができますが、断片化の根底にある原因をもう少し分析する必要がある場合に役立ちます。たとえば、3つまたは4つのタイプのオブジェクトを頻繁に割り当てると、メモリの断片化の問題が悪化する傾向にある場合は、それらをそれぞれのメモリプールに入れて、正しいサイズのメモリブロックを再利用できるようにすることができます。そうすることで、この特定のオブジェクトに合った一連のメモリブロックを用意し、オブジェクトXの割り当てによってYを保持するのに十分な大きさのメモリブロックを分割して、突然Yを割り当てることができないもう

(2)に関して、私はnedmallocの周りのラッパーを認識していません(率直に言って、私はnedmallocに慣れていません)が、あなた自身のラッパーを非常に簡単に作成することができます。新規および削除または過負荷/グローバル演算子の新規および削除の置換。私は後者の大きなファンではありませんが、あなたの割り当て "ホットスポット"がいくつかのクラスで構成されている場合は、それらを独自のクラス固有の演算子で新規および削除することは簡単です。

しかし、nedmallocは標準のmalloc/freeと少なくともMSコンパイラの代わりとして請求していますが、C++ランタイムライブラリはmalloc/freeに新しい/ deleteを転送すると思いますあなたの実行ファイルをnedmallocでビルドする

1

プログラム内でメモリが割り当てられている場所を調べる最も良い方法は、デバッガを使用することです。ロードされたすべてのDLLと実行可能ファイル自体には割り当てがあり、それらのすべてが仮想メモリを断片化します。さらに、C/C++ライブラリとWindows APIを使用すると、アプリケーションにヒープが作成されます。これにより、少なくとも仮想メモリのチャンクが確保されます。

たとえば、VirtualAllocを使用して、比較的小さなプログラムで仮想メモリの大きなチャンクを予約することができます.VirtualAllocが失敗した場合、または新しいDLL(など)をロードしようとすると、また、ロードするDLLとその場所を常に制御することはできません。多くのA/Vやその他の製品は、起動時に実行中のすべてのプロセスにDLLを注入します。これが起きると、これらのDLLはしばしば最初にロードアドレスで選択されます。つまり、デフォルトでコンパイル/リンクされている可能性が高くなります。典型的な32ビットWindowsアプリケーションの利用可能な2GBの仮想アドレス空間の中で、DLLがそのアドレス空間の途中でスタックを読み込むと、取得できる最大の単一の割り当て/予約は1GB未満になります。

windbgを使用すると、メモリのどの領域が消費され、予約されているかなどがわかります。lmコマンドは、すべてのDLLのロードアドレスとEXEとその範囲を表示します。 !vadumpコマンドは、プロセスとページ保護によって使用されるすべての仮想メモリを表示します。ページの保護はそこにあるものへの大きなヒントです。たとえば、64ビットのcalc.exeプロセスの次の(部分的な)!vadumpでは、最初の領域が単にアクセスから保護された仮想メモリの範囲であることがわかります。 (とりわけ、これにより、アドレス0にメモリが割り当てられなくなります。)MEM_COMMITは、メモリがRAMまたはページングファイルによってバックアップされることを意味します。 PAGE_READWRITEは、おそらくヒープ・メモリー、またはロードされたモジュールのデータ・セグメントです。 PAGE_READEXECUTEは通常、ロードされ、lmによって生成されたリストに現れるコードです。 MEM_RESERVEは、私はそれが物事を説明するのに役立ちます願って何かが

0:004> !vadump 
BaseAddress:  0000000000000000 
RegionSize:  0000000000010000 
State:    00010000 MEM_FREE 
Protect:   00000001 PAGE_NOACCESS 

BaseAddress:  0000000000010000 
RegionSize:  0000000000010000 
State:    00001000 MEM_COMMIT 
Protect:   00000004 PAGE_READWRITE 
Type:    00040000 MEM_MAPPED 

BaseAddress:  0000000000020000 
RegionSize:  0000000000003000 
State:    00001000 MEM_COMMIT 
Protect:   00000002 PAGE_READONLY 
Type:    00040000 MEM_MAPPED 

のメモリ領域を確保するVirtualAllocのと呼ばれるが、それは仮想メモリマネージャによってマップされていないこと、など...たことを意味します。 Windbgは素晴らしいツールであり、メモリの使用場所を見つけるのに役立つ多くの拡張機能を備えています。

本当にヒープを気にするなら、ヒープを見てください。

+0

ありがとう、私はwindbgを試してみます。 – Budric

2

メモリの断片化を減らすために、Windows Low-Fragmentation Heapを利用できます。私たちはこの製品にこれを使って良い効果をもたらしてきました。

+0

私はこの機能を見てきました。しかし、これを有効にするには、HeapSetInformation()を実行する必要があります。メモリのスナップショットはmain()の最初の行で取得され、すでにメモリは最初の1.3 GBのアドレス空間の後に断片化されています。プロセスエクスプローラで見ると、DLLやその他のものです。だから、LFHが助けてくれるかもしれませんが、DLLロードのためにすでに発生しているフラグメンテーションを防ぐことはできません。 – Budric

関連する問題