10

私のコードでは、多数のタスクを実行しますが、それぞれがデータを一時的に格納するために大量のメモリ配列を必要とします。私は約500のタスクを持っています。各タスクの開始時に、配列にメモリを割り当てます。

double[] tempDoubleArray = new double[M];

M は、タスクによって異なりますが、通常は 2000000 前後の大きな数値です。ここで、複雑な計算を行って配列を埋め、最後に配列を使用してこのタスクの結果を決定します。その後、tempDoubleArray は範囲外になります。

プロファイリングにより、配列を構築するための呼び出しに時間がかかることが明らかになりました。そこで、配列を静的にして再利用することで、配列を再利用することにしました。配列の最小サイズを把握するには、追加のジャグリングが必要であり、すべてのタスクを通過する追加のパスが必要ですが、機能します。現在、プログラムは大幅に高速化されています (すべてのタスクの実行が 80 秒から 22 秒に短縮されました)。

double[] tempDoubleArray = staticDoubleArray;

しかし、なぜこれがうまく機能するのか、私には少しわかりません。元のコードでは、tempDoubleArray が範囲外になると収集できるので、新しい配列を割り当てることはそれほど難しくないはずですよね?

なぜそれが機能するのかを理解することは、同じ効果を達成するための他の方法を理解するのに役立つかもしれず、割り当てがパフォーマンスの問題を引き起こすケースを知りたいからです。

4

3 に答える 3

7

収集できるからといって、収集できるとは限りません。実際、ガベージ コレクターがそのコレクションと同じくらい積極的だった場合、パフォーマンスは大幅に低下します。

配列の作成は、1 つの変数を作成するだけでなく、N変数 (N配列内の要素の数) を作成していることに注意してください。配列の再利用は、パフォーマンスを向上させるための費用対効果の高い方法ですが、慎重に行う必要があります。

明確にするために、「変数を作成する」とは具体的には、それらにスペースを割り当て、ランタイムがそれらを使用できるようにするために必要なすべてのステップを実行することです (つまり、値をゼロ/nullに初期化します)。配列は参照型であるため、ヒープに格納されるため、メモリ割り当てに関しては少し複雑になります。配列のサイズ (合計ストレージ領域が 85KB を超えるかどうか) に応じて、通常のヒープまたはラージ オブジェクト ヒープに格納されます。通常のヒープに格納された配列は、他のすべてのヒープ オブジェクトと同様に、ガベージ コレクションとヒープの圧縮をトリガーできます (これには、連続して利用可能なスペースを最大化するために、現在使用中のメモリをシャッフルすることが含まれます)。

于 2010-06-15T15:03:31.733 に答える
1

1 つの答えは、大きなオブジェクト ヒープです。85KB を超えるオブジェクトは、収集される頻度が低く、圧縮されない別の LOH に割り当てられます。

パフォーマンスへの影響に関するセクションを参照してください

  • 割り当てコストがあります (主に割り当てられたメモリのクリア)
  • コレクション コスト (LOH と Gen2 が一緒に収集されるため、Gen2 で大きなオブジェクトが圧縮されます)
于 2010-06-15T15:04:26.593 に答える
0

断片化が存在する場合、大きなメモリ ブロックを割り当てるのは必ずしも容易ではありません。確かなことは言えませんが、このような大きなメモリ ブロックに十分な連続したメモリを確保するには、何らかの再配置を行う必要があると思います。後続の配列の割り当てが速くない理由については、私の推測では、GC 時間と次の割り当ての間に大きなブロックが断片化されるか、元のブロックが最初から GC されていないかのいずれかです。

于 2010-06-15T15:05:39.463 に答える