27

効果的な Java は次のように述べています。

ファイナライザーを使用すると、パフォーマンスが大幅に低下します。

ファイナライザーを使用してオブジェクトを破棄するのが遅いのはなぜですか?

4

6 に答える 6

23

ガベージコレクターの仕組みのためです。パフォーマンスのために、ほとんどの Java GC はコピー コレクターを使用します。このコレクターでは、存続期間の短いオブジェクトがメモリの「eden」ブロックに割り当てられます。その世代のオブジェクトが収集されるとき、GC はオブジェクトをコピーするだけで済みます。より永続的なストレージスペースにまだ「生きている」ので、「eden」メモリブロック全体を一度に消去(解放)できます。ほとんどの Java コードはオブジェクト (ボックス化されたプリミティブ、一時配列など) の何千ものインスタンスをわずか数秒の有効期間で作成するため、これは効率的です。

ただし、ミックスにファイナライザーがある場合、GC は世代全体を一度に単純にワイプすることはできません。代わりに、ファイナライズが必要なその世代のすべてのオブジェクトを把握し、ファイナライザーを実際に実行するスレッドでそれらをキューに入れる必要があります。その間、GC はオブジェクトの効率的なクリーンアップを完了できません。そのため、オブジェクトを必要以上に長く存続させるか、他のオブジェクトの収集を遅らせるか、またはその両方を行う必要があります。さらに、ファイナライザーを実際に実行するための任意の待ち時間があります。

これらすべての要因が合計すると、重大なランタイム ペナルティが発生します。そのため、確定的なファイナライズ (close()オブジェクトの状態を明示的にファイナライズするメソッドなどを使用する) が通常は好まれます。

于 2010-05-18T19:00:03.383 に答える
10

実際にそのような問題に遭遇した:

Sun HotSpot JVM では、ファイナライザーは固定の低い優先順位が与えられたスレッドで処理されます。負荷の高いアプリケーションでは、ファイナライズが必要なオブジェクトを、優先度の低いファイナライズ スレッドが処理できるよりも速く作成するのは簡単です。一方、ファイナライズ保留中のオブジェクトによって使用されるヒープ上の領域は、他の用途には使用できません。ファイナライズを保留しているオブジェクトが利用可能なすべてのメモリを使用しているため、最終的には、アプリケーションはすべての時間をガベージ コレクションに費やす可能性があります。

これはもちろん、Effective Java で説明されているファイナライザーを使用しない他の多くの理由に加えてです。

于 2010-05-18T19:06:41.443 に答える
2

finalize()のドキュメントをよく読むと、ファイナライザーによってオブジェクトがGCによる収集を防ぐことができることがわかります。

ファイナライザーが存在しない場合、オブジェクトは単に削除することができ、それ以上の注意は必要ありません。ただし、ファイナライザーがある場合は、オブジェクトが再び「表示」されていないかどうかを後で確認する必要があります。

現在のJavaガベージコレクションがどのように実装されているかを正確に知らなくても(実際には、さまざまなJava実装があるため、さまざまなGCもあります)、オブジェクトにファイナライザーがある場合、GCは追加の作業を行う必要があると想定できます。この機能の。

于 2010-05-18T19:03:43.697 に答える
2

私は机から私のコピーEffective Javaを手に取って、彼が何を指しているのかを確認しました。

第 2 章のセクション 6 を読むと、さまざまなパフォーマンス ヒットについて詳しく説明しています。

You can't know when the finalizer will run, or even if it will at all. Because those resources may never be claimed, you will have to run with fewer resources.

このセクション全体を読むことをお勧めします。ここで説明するよりもはるかによく説明されています。

于 2010-05-18T19:02:16.083 に答える
1

私の考えは次のとおりです。Java はガベージ コレクション言語であり、独自の内部アルゴリズムに基づいてメモリの割り当てを解除します。頻繁に、GC はヒープをスキャンし、参照されなくなったオブジェクトを特定し、メモリの割り当てを解除します。ファイナライザーはこれを中断し、GC サイクルの外部でメモリの割り当てを強制的に解除するため、非効率になる可能性があります。ファイルハンドルを解放したり、決定論的に行う必要があるDB接続を閉じたりするなど、絶対に必要な場合にのみファイナライザーを使用することをお勧めします。

于 2010-05-18T18:57:37.813 に答える
0

私が考えることができる理由の 1 つは、リソースがすべて Java オブジェクトであり、ネイティブ コードではない場合、明示的なメモリ クリーンアップは不要であるということです。

于 2010-05-18T18:59:15.830 に答える