web-dev-qa-db-ja.com

ArrayListを空にするか、新しいリストを作成して、古いリストをガベージコレクションしますか?

コレクション(私の場合はArrayList)を空にすることと、新しいコレクションを作成する(ガベージコレクターに古いコレクションをクリアさせる)ことの利点と欠点は何ですか。

具体的には、listと呼ばれる_ArrayList<Rectangle>_があります。特定の条件が発生した場合、listを空にして、他のコンテンツでそれを補充する必要があります。 list.clear()を呼び出すか、新しい_ArrayList<Rectangle>_を作成して、古いものをガベージコレクションする必要がありますか?各アプローチの長所と短所は何ですか?

37
Cricketer

GCの負荷を減らしたい場合は、コンテナを保持してclearを呼び出します。clear()は配列内のすべての参照を無効にしますが、ガベージコレクター。これにより、ArrayList内の配列を大きくする必要がないため、今後の挿入が高速化される可能性があります。このアプローチは、コンテナに追加する予定のデータが、消去するのとほぼ同じサイズである場合に特に有利です。

さらに、クリアしようとしている配列への参照を他のオブジェクトが保持している場合は、clearを使用する必要があります。

コンテナを解放して新しいコンテナを作成することは、新しいデータのサイズが以前とは異なる場合に意味があります。もちろん、clear()と組み合わせてtrimToSize()を呼び出すことで、同様の効果を実現できます。

36
dasblinkenlight

ArrayListをリサイクルすることの利点は(たとえばclearを呼び出すことにより)、新しいものを割り当てるオーバーヘッドと、それを成長させるコストを回避できることです...良いinitialCapacityヒント。

ArrayListをリサイクルすることの欠点は次のとおりです。

  • clear()メソッドは、nullsバッキング配列の各(使用済み)スロットにArrayListを割り当てる必要があります。

  • clear()は、メモリを解放するためにバッキング配列のサイズを変更しません。そのため、リストを繰り返し埋めたり消去したりすると、最終的には、検出された最大のリストを表すのに十分なメモリを使用することになります。つまり、メモリフットプリントが増加しました。 trimToSize()を呼び出すことで対処できますが、ガベージオブジェクトなどを作成します1

  • パフォーマンスに影響を与える可能性のあるローカリティおよび世代間の問題があります。 ArrayListを繰り返しリサイクルすると、オブジェクトとそのバッキング配列が終身保有される可能性があります。つまり:

    • リストオブジェクトとリスト要素を表すオブジェクトは、ヒープの異なる領域にある可能性が高く、特にGC時にTLBミスとページトラフィックが増加する可能性があります。

    • (若い世代の)参照を(終身の)リストのバッキング配列に割り当てると、GC実装によっては書き込みバリアのオーバーヘッドが発生する可能性があります。

実際のアプリケーションのパフォーマンスのトレードオフを正確にモデル化することはできません。変数が多すぎます。ただし、「受け取った知恵」は、メモリが十分にある場合、リサイクルは通常良いアイデアではないということです2 と半分まともなガベージコレクタ。

また、最新のJVMは非常に効率的にオブジェクトを割り当てることができることにも注意してください。単にヒープの「空き」ポインタに更新し、2つまたは3つのオブジェクトヘッダーワードを書き込むだけです。メモリのゼロ化はGCによって行われます...それを行う際の作業に加えて、大体は、リスト内の参照をnullにするためにclear()が行う作業と同等です。リサイクルされています。


1-clear()に続いてtrimToSize(...)を呼び出すよりも、新しいArrayListを作成する方がパフォーマンスに優れています。後者を使用すると、ガベージコレクションのオーバーヘッドと、不要なヌルのオーバーヘッドの両方が発生します。

2-ガベージオブジェクトの非ガベージオブジェクトに対する割合が高い場合、コピーコレクタはより効率的です。この種のコレクターの動作を分析すると、到達可能なオブジェクトを見つけてコピーする際に、ほとんどすべてのコストが発生します。ガベージオブジェクトに対して行う必要がある唯一のことは、新しいオブジェクトの割り当ての準備ができた、退避された「from」スペースをブロックゼロ書き込みすることです。


(ガベージ)オブジェクトの作成率を最小限に抑える必要がある場合を除き、ArrayListオブジェクトをリサイクルしないことをお勧めします。例えば(有害な)GCの一時停止を減らすための唯一のオプションだからです。

すべてが平等であり、最新のHotspot JVMでは、次のことを行うことで最高のパフォーマンスが得られるというのが私の理解です。

  • リサイクルではなく、新しいArrayListオブジェクトを割り当てます。
  • リストオブジェクトを割り当てるときは、正確なinitialSizeヒントを使用してください。わずかに過小評価するよりも、わずかに過大評価する方が適切です。
47
Stephen C

おもしろいポイントがすでに書かれているので、さらに1レベル深く考えることができます。

ディスラプターパターンに関する記事を読むよりも気付いていません。 LMAXのディスラプターパターンはどのように機能しますか?

reuse基礎となるcollectionだけでなく、reuseコレクション内のentities.

例えば。プロデューサーとコンシューマーのユースケースを想定してください。プロデューサーは、同じ(循環)配列に何度もデータを入力し、同じエンティティを使用することさえできます。プロパティ、内部状態をクリアし、独自に入力します。

GCの観点から見ると、1レベル優れたソリューションです。しかし、これは明らかにすべての問題に役立つわけではない特別なケースです。

1
Martin Podval