2008-09-17 3 views
11

私は、ネットワークからメッセージを受信し、メッセージタイプに応じてそれらを異なるオブジェクトに変換し、最後にアプリケーションビジネスロジックを適用する金融C#アプリケーションを作成しています。リアルタイム.NETアプリケーションでガベージコレクションを回避するにはどうすればいいですか?

ビジネスロジックを適用した後、私はこのインスタンスを再び必要としないと確信しています。ガベージコレクタが解放するのを待つのではなく、明示的に「削除」したいと思います。

C#でこれを行うより良い方法は、オブジェクトのプールを使用して常に同じインスタンスセットを再利用するか、より良い戦略があるかどうかです。

時間の重大なプロセス中にCPUを使用するガベージコレクションを避けることを目標としています。

+0

では、(a)所定のオブジェクトの作成が遅い、(b)32ビットアプリケーションの場合、メモリの断片化を減らすため、またはデータを使用する配列/リスト/ブロック> 64 KB、例えば要素あたり8K以上8B、要素あたり16K要素×4B。 .Net **は、毎回異なるサイズを要求すると、断片化したメモリにつながる可能性のある大きなブロック**を再配置(移動)しません。十分な大きさの要素を前面に配置し、それらを保持することはより安全です。 (ただし、使用していないときはフィールドをクリアするので、GCは指定したものを取り戻すことができます)。割り当てられたサイズを超える場合は、#要素を2倍します。 – ToolmakerSteve

+0

極端なケースでは、(私は64ビットで動作しない依存関係のために)1つの32ビットアプリケーションを持っているので、メモリの断片化を避けるために、各サブリストが64 KBに収まるようにリストのリストを保持して、.Netが "ラージオブジェクトヒープ"に割り当てないようにします。したがって、アプリケーションの4 GBアドレス空間に大きな「穴」を作成するのではなく、GC中にメモリを「コンパクト」にすることができます。この特定のアプリケーションは、ネイティブ(非.NET)メモリからDirectXの割り当てを多く含んでいたため、問題がありました。 non.Netと.Netの割り当てが混在すると、断片化が発生しました。 – ToolmakerSteve

答えて

21

すぐに削除しないでください。各オブジェクトのガベージコレクタを呼び出すことは悪い考えです。通常、は本当にですが、ガベージコレクタはまったく混乱したくなく、時間的に重要なプロセスであっても、そのプロセスが敏感ならば競合状態になります。

アプリの負荷が軽い場合、軽い負荷がかかることがわかっている場合は、次の忙しい時間前にクリーンアップを促すために軽い期間になると、より一般的なGC.Collect()を試すことがあります。

+8

GC.Collectが決定論的ガベージコレクションを提供していないことを理解している限り、これは問題ありません。あなたはそのサイクルを実行するようにGCに指示します。この場合、メモリを収集するかどうかが決まります。 GC.Collectの呼び出しでは、メモリのコレクションが保証されません。 – JeremiahClark

+1

ノンディターミリズムを示す言い換え。 –

+1

** GC.Collect()を頻繁に呼び出すことは、よくあることよりも害を及ぼす可能性が高いことに注意してください**:まだ参照を持つオブジェクトは、世代0からgen 1、またはgen 1からgen 2に移動されます。 GCの長時間の使用が必要になります。 GC.Collect()は、メッセージがまだ処理されていない場合にのみ呼び出して、一時データへの参照がないようにします。 – ToolmakerSteve

18

はここを見て:http://msdn.microsoft.com/en-us/library/bb384202.aspx

あなたは、現時点では重要な何かをやっているガベージコレクタを伝えることができ、そしてそれはあなたにいいことしようとします。

+2

良いブログエントリhttp://blogs.microsoft.co.il/blogs/sasha/archive/2008/08/10/low-latency-gc-in-net-3-5.aspx –

1

プール内の各タイプのインスタンスの数が限られていて、すでに完了しているインスタンスを再利用することができます。プールのサイズは、処理するメッセージの量によって異なります。

5

強制的にGC.Collect()を実行するのは悪い考えですが、GCをそのまま実行してください。必要に応じて成長できるオブジェクトのプールを使用するのが最善の解決策であるように思えます。私はこのパターンをうまく使いました。

このようにすると、ガベージコレクションだけでなく、通常の割り当てコストも回避できます。

最後に、GCが問題を引き起こしていますか?あなたはおそらく測定し、これを証明する必要がありますすべてのperf節約ソリューションを実装する前に - あなたは自分自身の不必要な仕事を引き起こしている可能性があります!

0

理論上、CPUが負荷のかかっているか、本当に必要な場合以外は、GCを実行しないでください。しかし、そうしなければならない場合は、すべてのオブジェクトをメモリに保持することができます。おそらくシングルトンのインスタンスにすることもできます。これはおそらく、GCの実行時を保証する唯一の方法です。

2

C/C++のような確定的なプラットフォームを使用するのが時間的に厳しい場合は、 GC.Collect()を呼び出してもCPUサイクルが生成されます。

あなたの質問は、メモリを節約してオブジェクトを取り除きたいという提案から始まります。これはスペースのクリティカルな最適化です。あなたはGCが人間よりもこの状況を最適化する上で優れているので、あなたが本当に望むものを決める必要があります。

1

の代わりに、あなたが既に使用されているオブジェクトを再利用しない理由メッセージを取得するたびに、オブジェクトの新しいインスタンスを作成しますか?この方法では、ガベージコレクタとの闘いは行われず、ヒープメモリは断片化されません。**

メッセージタイプごとに、使用されていないインスタンスを保持するプールを作成できます。ネットワークメッセージを受信するたびに、メッセージタイプを調べ、待機中のインスタンスを適切なプールから取り出してビジネスロジックを適用します。その後、メッセージオブジェクトのインスタンスをプールに戻します。

あなたのコードを容易にスケーリングできるように、インスタンスでプールを「怠惰に読み込み」たいと思うでしょう。したがって、プール・クラスは、ヌル・インスタンスがプルされた時点を検出し、それを渡す前にそれを検出する必要があります。呼び出し元のコードがプールに戻すと、実際のインスタンスになります。

** "オブジェクトプーリングは、オブジェクトを割り当ておよび割り当て解除するのではなく再利用できるようにするためのパターンです。ヒープの断片化や高価なGC圧縮を防ぐのに役立ちます。ガベージコレクタを第二推測しようとすると

http://geekswithblogs.net/robp/archive/2008/08/07/speedy-c-part-2-optimizing-memory-allocations---pooling-and.aspx

7

は、一般的には非常に悪い考えです。 Windowsでは、ガベージコレクタはa generational oneであり、かなり効率的であると信じられます。この一般的なルールにはいくつかの例外があります。最も一般的なことは、事実があるとわかっているワンタイムイベントが発生すると、多くの古いオブジェクトが死ぬ原因になることです。オブジェクトがGen2(一番長く生きている)彼らはハングアップする傾向があります。

あなたが言及した場合、あなたは短命のオブジェクトの数を生成しているように聞こえます。これらはGen0コレクションになります。とにかくこれらは比較的頻繁に起こり、最も効率的です。必要に応じて再利用可能なオブジェクトプールを用意することで回避できますが、GCがパフォーマンス上の問題であるかどうかを確かめてから、CLRプロファイラがこれを実行するツールとなります。

ガベージコレクタは、さまざまな.NETフレームワーク上で異なる点に注意してください。コンパクトなフレームワーク(Xbox 360とモバイルプラットフォーム上で動作します)では非世代のGCであり、あなたのプログラムが生成するゴミについてもっと注意してください。

11

あなたは自分でヒットします - オブジェクトのプールを使用し、それらのオブジェクトを再利用します。これらのオブジェクトへの呼び出しのセマンティクスは、ファクトリファサードの背後に隠れる必要があります。あらかじめ定義された方法でプールを拡張する必要があります。おそらく限界に達するたびにサイズを倍増させます - 高い水アルゴリズム、または一定のパーセンテージ。 GC.Collect()を呼び出さないように強くお勧めします。

プールの負荷が十分に低くなるとプールが縮小され、最終的にガベージコレクションがトリガーされるため、CLRはそれを心配してください。

2

その音から、C#では存在しない確定的なファイナライズ(C++のデストラクタ)について話しているようです。あなたがC#で見つける最も近いものは使い捨てのパターンです。基本的にはIDisposableインターフェイスを実装します。

基本的なパターンはこれです:アプリは

public class MyClass: IDisposable 
{ 
    private bool _disposed; 

    public void Dispose() 
    { 
     Dispose(true); 
     GC.SuppressFinalize(this); 
    } 

    protected virtual void Dispose(bool disposing) 
    { 
     if(_disposed)  
      return; 

     if(disposing) 
     { 
      // Dispose managed resources here 
     } 

     _disposed = true; 
    } 
} 
+0

あなたがクラスにはプロパティとして他の参照型がありますか?ガーベジコレクションのために残しておくか、処分するとすぐに処理されますか? – jocull

+1

@jocull「Dispose」はガベージコレクションとはまったく関係ありません。できるだけ最善の方法は、* un *管理されたリソースを決定論的に処理することです(もちろん、その場合はファイナライザを用意する必要があります)。確定的な唯一の部分は、管理されていないリソースです。管理されたメモリは常に管理されます。これにより、たとえば参照が残っているものを誤って削除しないようにします。スタックさえも保証されません*。実装の詳細であり、仕様の一部ではありません。 – Luaan

2

どのように負荷をかけますか?私は3つのサウンドカード(Managed DirectX、44.1KHz、ステレオ、16ビット)を8KBのブロックでキャプチャし、3つのストリームのうち2つをTCP/IP経由で別のコンピュータに送信するアプリケーションを作成しました。 UIは、3つのチャンネルのそれぞれについてオーディオレベルメーターおよび(スムーズな)スクロールタイトル/アーティストをレンダリングする。これは、XP、1.8GHz、512MBなどのPCで動作します。アプリケーションは、CPUの約5%を使用します。

GCメソッドを手動で呼び出すのは難しいです。しかし、私は無駄だったいくつかのものを調整しなければならなかった。私はRedGateのAntプロファイラを使って無駄な部分を取り除きました。すばらしいツール!

事前に割り当てられたバイト配列のプールを使用したかったのですが、管理対象のDXアセンブリは内部的にバイトバッファを割り当て、それをアプリケーションに返します。私はする必要がないことが分かった。

+0

完全に非astroturf応答が、私は元のポスターに似た問題に直面しているので、私はこの質問を読んでいる、最初のものの一つはアリだった。 –

+0

44.1kHzのステレオ16ビットサンプルストリーム用の8kBバッファは約45msの遅延をもたらします。それはリアルタイムからは遠いです。あなたはネイティブプログラミングを割り当てていない限り、5ミリ秒以下になるのはずっと面倒です。 –

5

Q「の目標は、タイムクリティカルなプロセス中の任意のCPUを使用するようにガベージコレクションを避けるためであること」:重要な時期で、あなたは、ハードウェアのいくつかの難解な作品を聴いているわけ場合、あなたは割込みを逃す余裕はありませんか?

A: C#が使用する言語ではない場合、そのためにアセンブラ、CまたはC++が必要です。

Q:時間がクリティカルな場合は、パイプに多数のメッセージがあり、ガーベジコレクタが処理を遅くしたくないということを意味しますか?

A:もしそうなら、あなたは不必要に心配しています。オブジェクトの寿命が非常に短いということによって、ガベージコレクタはパフォーマンスを大幅に遅らせることなく、非常に効率的にリサイクルされます。

しかし、確かに知っている唯一の方法は、テストメッセージです。一定のストリームのテストメッセージを処理するように設定してください.GPが実行されたときにパフォーマンス統計が表示される場合は気になりますあなたがそれを見分けることができたとしても、私は実際に問題があればさらに驚くでしょう)。

関連する問題