13

おそらく他の未知のものの中でLOHフラグメンテーションを含む可能性のあるいくつかの絶望的なメモリの問題に関して、ここに他の活発な質問があります。

今の私の質問は、物事を行うための受け入れられた方法は何ですか?私のアプリをVisualC#で実行する必要があり、int [4000000]に合わせて大きな配列を処理する必要がある場合、ガベージコレクターがLOHを処理することを拒否することで、どうして運命づけられないのでしょうか。

大きな配列をグローバルにすることを余儀なくされているように思われ、それらの周りに「新しい」という言葉を使用することは決してありません。そのため、関数によって渡される適切なサイズの配列ではなく、「maxindex」変数を使用した不適切なグローバル配列が残ります。

これは悪い習慣だといつも言われてきました。どのような選択肢がありますか?

曲に何らかの機能はありSystem.GC.CollectLOH("Seriously")ますか?ガベージコレクションをSystem.GC以外にアウトソーシングする方法はありますか?

とにかく、大きな(> 85Kb)変数を処理するために一般的に受け入れられているルールは何ですか?

4

6 に答える 6

31

まず、ガベージコレクターLOHを収集するため、その存在にすぐに怖がらないでください。LOHは、第2世代が収集されるときに収集されます。

違いは、LOHが圧縮されないことです。つまり、寿命の長いオブジェクトがそこにある場合、LOHをこのオブジェクトの前の領域と後の領域の2つのセクションに効果的に分割できます。この動作が続く場合は、存続期間の長いオブジェクト間のスペースが後続の割り当てに十分な大きさではなく、.NETが大きなオブジェクト、つまりLOHを配置するためにますます多くのメモリを割り当てる必要があるという状況に陥る可能性があります。断片化されます。

さて、そうは言っても、LOHの端の領域にライブオブジェクトが完全にない場合、LOHのサイズが縮小する可能性があるため、唯一の問題は、オブジェクトを長時間(たとえば、アプリケーションの期間)そこに置いたままにする場合です。

.NET 4.5.1以降、LOHを圧縮できます。GCSettings.LargeObjectHeapCompactionModeプロパティを参照してください。

LOHの断片化を回避するための戦略は次のとおりです。

  • ぶらぶらする大きなオブジェクトを作成することは避けてください。基本的に、これは大きな配列、または大きな配列をラップするオブジェクト(バイト配列をラップするMemoryStreamなど)を意味します。他にそれほど大きなものはありません(複雑なオブジェクトのコンポーネントはヒープに個別に格納されるため、非常に大きくなることはめったにありません)。また、内部で配列を使用する大きな辞書やリストにも注意してください。
  • ダブルアレイに注意してください—これらがLOHに入るしきい値ははるかに小さく、正確な数値は思い出せませんが、数千にすぎません。
  • MemoryStreamが必要な場合は、1つの大きな配列ではなく、いくつかの小さな配列に戻るチャンクバージョンを作成することを検討してください。IListとIDictionaryのカスタムバージョンを作成することもできます。これは、チャンクを使用して、最初にLOHになってしまうことを回避します。
  • RemotingはMemoryStreamsを多用するため、呼び出しの長さの間にLOHをフラグメント化する可能性があるため、非常に長いRemoting呼び出しは避けてください。
  • 文字列のインターンに注意してください—何らかの理由で、これらはLOHにページとして保存され、アプリケーションがインターンする新しい文字列に遭遇し続ける場合、つまり文字列のセットが有限であることがわかっていない限り、string.Internの使用を避けると、深刻な断片化を引き起こす可能性があります。フルセットは、アプリケーションの寿命の早い段階で発生します。(私の以前の質問を参照してください。)
  • Son of Strikeを使用して、LOHメモリを正確に使用しているものを確認します。これを行う方法の詳細については、この質問をもう一度参照してください。
  • 大きな配列をプールすることを検討してください。

編集:ダブルアレイのLOHしきい値は8kのようです。

于 2010-03-08T15:59:47.167 に答える
8

これは古い質問ですが、.NETで導入された変更で回答を更新しても問題はないと思います。ラージオブジェクトヒープを最適化できるようになりました。明らかに、最初の選択は、最良の設計選択が行われたことを確認することですが、今このオプションがあるのは素晴らしいことです。

https://msdn.microsoft.com/en-us/library/xe0c2357(v=vs.110).aspx

「.NETFramework4.5.1から、次の例に示すように、Collectメソッドを呼び出す前にGCSettings.LargeObjectHeapCompactionModeプロパティをGCLargeObjectHeapCompactionMode.CompactOnceに設定することで、ラージオブジェクトヒープ(LOH)を圧縮できます。」

GCSettingsは、System.Runtime名前空間にあります。

GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
GC.Collect(); 
于 2015-04-08T16:20:29.357 に答える
7

最初に頭に浮かぶのは、配列を小さな配列に分割して、GCがLOHを配置するために必要なメモリに到達しないようにすることです。配列をたとえば10,000の小さな配列に吐き出し、渡したインデクサーに基づいてどの配列を調べるかを認識するオブジェクトを作成できます。

今はコードを見ていませんが、なぜそれほど大きな配列が必要なのか疑問に思います。コードのリファクタリングを検討する可能性があるので、そのすべての情報を一度にメモリに保存する必要はありません。

于 2010-03-08T15:18:56.913 に答える
5

あなたはそれを間違えます。配列サイズが4000000である必要はなく、garbaceコレクターを呼び出す必要はありません。

  • 独自のIList実装を作成します。「PagedList」のように
  • 65536要素の配列にアイテムを格納します。
  • ページを保持する配列の配列を作成します。

これにより、基本的にすべての要素に1回のリダイレクトでアクセスできます。そして、個々のアレイが小さいので、断片化は問題ではありません...

...もしそうなら...それならページを再利用してください。それらを廃棄時に捨てないでください。静的な「PageList」に配置して、最初にそこからプルしてください。これはすべて、クラス内で透過的に実行できます。

本当に良いことは、このリストがメモリ使用量においてかなり動的であることです。ホルダー配列(リダイレクタ)のサイズを変更することをお勧めします。そうでない場合でも、1ページあたり約512kbdataです。

第2レベルの配列は、基本的に1バイトあたり64kです。これは、クラスの場合は8バイト(ページあたり512kb、32ビットの場合は256kb)、または構造体バイトあたり64kbです。

技術的に:

int[]をint[][]に変換します

必要に応じて32ビットと64ビットのどちらが優れているかを判断します;)長所と短所の両方があります。

そのような1つの大きな配列を処理することは、どの言語でも扱いにくいです-もしあなたがそうするなら、それなら...基本的に....プログラムの開始時に割り当て、決して再作成しないでください。唯一の解決策。

于 2010-03-08T15:29:05.697 に答える
1

これは古い質問ですが、.NET Standard 1.1(.NET Core、.NET Framework 4.5.1+)には、別の可能な解決策があります。

パッケージで使用ArrayPool<T>すると、この問題を回避するために配列をプールできます。System.Buffers

于 2019-12-22T12:15:34.330 に答える
0

問題がどのように発生する可能性があるかという観点から、上記の回答に詳細を追加しています。LOHの断片化は、オブジェクトが長寿命であることに依存するだけでなく、複数のスレッドがあり、それぞれがLOHに入る大きなリストを作成している状況では、最初のスレッドがリストを増やす必要がありますが、メモリの次の連続ビットはすでに2番目のスレッドからリストによって占有されているため、ランタイムは最初のスレッドリストに新しいメモリを割り当てます-かなり大きな穴を残します。これは、私が継承した1つのプロジェクトで現在起こっていることです。したがって、LOHは約4.5 MBですが、ランタイムには合計117 MBの空きメモリがありますが、最大の空きメモリセグメントは28MBです。

複数のスレッドがなくてもこれが発生する可能性のある別の方法は、ある種のループで複数のリストが追加され、それぞれが最初に割り当てられたメモリを超えて拡張すると、割り当てられたスペースを超えて成長するにつれて、それぞれが互いに跳躍することです。 。

便利なリンクは次のとおりです。https ://www.simple-talk.com/dotnet/.net-framework/the-dangers-of-the-large-object-heap/

まだこれに対する解決策を探していますが、1つのオプションは、ある種のプールされたオブジェクトを使用して、作業を行うときにプールから要求することです。大きな配列を扱っている場合、別のオプションは、カスタムコレクション、たとえばコレクションのコレクションを開発することです。これにより、巨大なリストが1つだけではなく、それぞれがLOHを回避する小さなリストに分割されます。

于 2013-05-16T10:26:14.360 に答える