質問
長時間実行されているサーバー プロセス (Windows Server 2003 上で実行されている) がメモリ割り当てエラーにより例外をスローするという問題が時折発生しています。私たちは、メモリの断片化が原因でこれらの割り当てが失敗しているのではないかと考えています。
したがって、私たちは役立つ可能性のある代替メモリ割り当てメカニズムをいくつか検討してきました。誰かが最適なものを教えてくれることを期待しています。
1) Windowsを使用する 低断片化ヒープ
2) jemalloc - で使用される Firefox 3
3) ダグ・リーの マロック
私たちのサーバー プロセスはクロスプラットフォーム C++ コードを使用して開発されているため、どのソリューションもクロスプラットフォームであることが理想的です (*nix オペレーティング システムはこの種のメモリ断片化に悩まされますか?)。
また、LFH が Windows Server 2008 / Vista のデフォルトのメモリ割り当てメカニズムになっていると考えるのは正しいでしょうか?...お客様がサーバー OS をアップグレードするだけで、現在の問題は「解消」されますか?
解決
まず、私はリソースの漏洩を示唆した他の投稿者に同意します。まずはそれを除外したいと考えています。
現在使用しているヒープ マネージャーには、ヒープ内で利用可能な実際の空き領域の合計をダンプする方法があるといいのですが、 無料 ブロック)、および分割されるブロックの総数も表示されます。平均空きブロック サイズがヒープ内の合計空き領域に比べて比較的小さい場合は、断片化の問題が発生しています。あるいは、最大の空きブロックのサイズをダンプし、それを合計空き領域と比較することができれば、同じことが達成できます。最大の空きブロックは、合計に比べて小さくなります。 無料 断片化が発生している場合は、すべてのブロックにわたって利用可能なスペースが確保されます。
上記について明確にしておきますが、すべての場合において、私たちは次のことについて話しています。 無料 ヒープ内に割り当てられたブロックではなく、ヒープ内のブロックです。いずれにしても、上記の条件が満たされない場合は、 する 何らかの漏れ状況がある。
したがって、リークを除外したら、より優れたアロケーターの使用を検討できます。 ダグ・リーのmalloc 質問で提案されているのは、一般的な用途に非常に優れたアロケータであり、非常に堅牢です ほとんど 当時の。言い換えれば、ほとんどのアプリケーションで非常にうまく機能することが実証されています。ただし、どのようなアルゴリズムも理想的ではありません。 全て アプリケーションや管理アルゴリズムのアプローチは、その設計に反する適切な病理学的条件によって壊れる可能性があります。
断片化の問題が発生するのはなぜですか? - 断片化の問題の原因は次のとおりです。 引き起こされた これはアプリケーションの動作によって異なり、同じメモリ領域での割り当て有効期間が大きく異なることに関係します。つまり、一部のオブジェクトは定期的に割り当てられ、解放されますが、他の種類のオブジェクトはすべて同じヒープ内に長期間存続します。存続期間が長いオブジェクトは、アリーナのより広い領域に穴を開け、それにより、解放された隣接するブロックの合体。
この種の問題に対処するには、ヒープを論理的にライフタイムがより類似したサブアリーナに分割することが最善の方法です。実際には、一時的なヒープと永続的なヒープ、または同様の存続期間を持つものをグループ化するヒープが必要になります。
他の人は、問題を解決するために、割り当てサイズをより類似または同一にするという別のアプローチを提案していますが、これは内部フラグメンテーションと呼ばれる異なる種類のフラグメンテーションを作成するため、あまり理想的ではありません。これは実際には、無駄なスペースを持っています。ブロック内に必要以上のメモリを割り当てることによって。
さらに、Doug Lea のような優れたヒープ アロケータでは、ブロック サイズをより類似させる必要はありません。アロケータはすでに 2 のべき乗サイズのバケット化スキームを実行しているため、malloc( ) - 実際には、ヒープ マネージャーは、アプリケーションが調整を行うよりもはるかに堅牢に自動的にそれを実行します。
他のヒント
メモリリークの可能性を誤って早期に除外してしまったように思います。わずかなメモリ リークでも、深刻なメモリの断片化が発生する可能性があります。
アプリケーションが次のように動作すると仮定します。
10MBを割り当てる
1バイトを割り当てる
無料 10MB
(おっと、1 バイトを解放しませんでしたが、1 バイトのことなど誰が気にするでしょうか)
かなり微量の漏れのようですが、 割り当てられたメモリの合計サイズだけを監視している場合は、ほとんど気付かないでしょう。。しかし、このリークにより、アプリケーションのメモリは最終的に次のようになります。
.
.
無料 - 10MB
.
.
[割り当て-1バイト]
.
.
無料 - 10MB
.
.
[割り当て-1バイト]
.
.
無料 - 10MB
.
.
この漏れは気付かれないでしょうね…11MBを割り当てるまで
ミニダンプに完全なメモリ情報が含まれていると仮定すると、次の使用をお勧めします。 デバッグ診断 漏れの可能性を特定します。生成されたメモリ レポートでは、 割り当て数 (サイズではなく) を注意深く調べてください。.
あなたが示唆しているように、Doug Lea の malloc はうまく機能するかもしれません。これはクロスプラットフォームであり、出荷コードで使用されています。少なくとも、テスト用にコードに簡単に統合できる必要があります。
固定メモリ環境で何年も作業してきたので、非固定環境であっても、この状況は確かに問題です。CRT アロケータは、パフォーマンス (速度、無駄なスペースの効率など) の点でかなり悪い傾向にあることがわかりました。長期にわたって優れたメモリ アロケータが広範囲に必要な場合は、独自のメモリ アロケータを作成する (または dlmalloc のようなものが機能するかどうかを確認する) べきであると私は強く信じています。重要なのは、割り当てパターンに合わせて機能するものを作成することです。これは、他のほとんどすべてのことと同様に、メモリ管理の効率性と関係があります。
dlmalloc を試してみてください。間違いなく高く評価します。かなり調整可能であるため、コンパイル時オプションの一部を変更することで効率が向上する可能性があります。
正直なところ、新しい OS の実装で物事が「なくなる」ことに依存すべきではありません。N 年後のサービス パック、パッチ、または別の新しい OS によって、問題がさらに悪化する可能性があります。繰り返しになりますが、堅牢なメモリ マネージャーを必要とするアプリケーションの場合は、コンパイラーで利用可能なストック バージョンを使用しないでください。最適なものを見つけてください あなたの 状況。dlmalloc から始めて調整し、状況に最適な動作が得られるかどうかを確認します。
割り当て解除する量を減らすことで、断片化を減らすことができます。
例えばたとえば、サーバー側スクリプトを実行している Web サーバーの場合、ページを出力する文字列が作成される場合があります。ページリクエストごとにこれらの文字列を割り当てたり割り当て解除したりするのではなく、文字列のプールを維持するだけです。そのため、さらに必要な場合にのみ割り当てますが、割り当て解除はしません(つまり、しばらくすると割り当ても解除されない状況になります。十分)
_CrtDumpMemoryLeaks(); を使用できます。デバッグ ビルドの実行時にメモリ リークをデバッグ ウィンドウにダンプしますが、これは Visual C コンパイラに固有のものだと思います。(crtdbg.hにあります)
断片化を疑う前に、リークを疑った方が良いと思います。
メモリ集約型のデータ構造の場合は、再利用可能なストレージ プール メカニズムに切り替えることができます。ヒープではなくスタックにより多くのものを割り当てることもできるかもしれませんが、実際には大きな違いは生じないと思います。
valgrind などのツールを起動するか、集中的なログを実行して、解放されていないリソースを探します。
@nsaners - 問題はメモリの断片化にあると確信しています。分析しました ミニダンプ これは、大きな (5 ~ 10 MB) メモリチャンクが割り当てられているときに問題が発生していることを示しています。また、プロセス (オンサイトおよび開発中) を監視してメモリ リークをチェックしましたが、何も検出されませんでした (メモリ フットプリントは一般的に非常に低いです)。
この問題は Unix でも発生しますが、通常はそれほど深刻ではありません。
低断片化ヒープは私たちを助けてくれましたが、同僚はそう誓います スマートヒープ(何年もの間、いくつかの製品でクロスプラットフォームで使用されてきました)。残念ながら、今回は別の事情により、Smart Heap を使用することができませんでした。
また、ブロック/チャンキングの割り当てと範囲に精通したプール/戦略、すなわちここでの長期的なもの、そこには全体の要求、短期的なものなどを見ていきます。
いつものように、速度を上げるためにメモリを浪費することもあります。
この手法は汎用アロケータには役に立ちませんが、その場所はあります。
基本的に、すべての割り当てが同じサイズであるプールからメモリを返すアロケーターを作成するという考え方です。どのブロックも他のブロックと同等であるため、このプールは断片化することはありません。異なるサイズのチャンクを持つ複数のプールを作成し、要求された量より大きい最小のチャンク サイズのプールを選択することで、メモリの無駄を減らすことができます。私はこのアイデアを使用して、O(1) で実行されるアロケーターを作成しました。
シンプルで手っ取り早い解決策は、アプリケーションを分割することです。 いくつかのプロセス, 、プロセスを作成するたびに新しい HEAP を取得する必要があります。
メモリと速度が少し低下する可能性があります (スワップ) が、高速なハードウェアと大きな RAM があれば解決できるはずです。
これは、スレッドがまだ存在していなかった時代の、デーモンを使用した古い UNIX のトリックでした。
Win32 について話している場合は、LARGEADDRESSAWARE を使用して何かを圧迫してみることができます。最適化されたメモリが最大 1 GB 追加されるため、アプリケーションの断片化が長くなります。