この質問を読んだ後、Java を教えられたときのことを思い出し、finalize() を呼び出したり、ガベージ コレクターを実行したりしないように言われました。誰かがこれの理由を数文に要約できますか? この件に関する Sun のテクニカル レポートを読むことはできると思いますが、簡潔で簡潔な回答があれば、私の好奇心を満足させることができると思います。
7 に答える
簡単に言えば、Java ガベージ コレクションは非常に細かく調整されたツールです。System.gc() は大ハンマーです。
Java のヒープは異なる世代に分割され、それぞれが異なる戦略を使用して収集されます。プロファイラーを正常なアプリにアタッチすると、ほとんどのオブジェクトが若い世代の高速コピー コレクターによってキャッチされるため、最も高価な種類のコレクションを実行する必要がほとんどないことがわかります。
System.gc() を直接呼び出すと、技術的には何も行われないことが保証されていますが、実際には、コストがかかり、ストップ・ザ・ワールドのフル・ヒープ・コレクションがトリガーされます。これは、ほとんどの場合、間違った方法です。リソースを節約していると思っていても、実際には正当な理由もなくリソースを浪費しており、Java にすべてのライブ オブジェクトを「念のため」再チェックさせています。
重大な瞬間に GC の一時停止に問題がある場合は、一時停止に費やされる時間を最小限に抑えるように特別に設計されたコンカレント マーク/スイープ コレクターを使用するように JVM を構成することをお勧めします。それをさらに壊します。
あなたが考えていた Sun のドキュメントはここにあります: Java SE 6 HotSpot™ Virtual Machine Garbage Collection Tuning
(ご存じないかもしれませんが、オブジェクトに finalize() メソッドを実装するとガベージ コレクションが遅くなります。まず、オブジェクトを収集するために2 回のGC 実行が必要です。第 2 に、finalize() メソッドを持つオブジェクトは、個別に収集する必要があり、まとめて破棄することはできないため、GC によって特別なケースとして扱われる必要があります。)
ファイナライザーを気にしないでください。
インクリメンタル ガベージ コレクションに切り替えます。
ガベージ コレクターを助けたい場合は、不要になったオブジェクトへの参照を null オフにします。たどるパスが少ない=より明確にガベージ。
(非静的) 内部クラス インスタンスは、親クラス インスタンスへの参照を保持することを忘れないでください。したがって、内部クラスのスレッドは、予想よりも多くの荷物を保持します。
非常に関連した流れで、シリアライゼーションを使用していて、一時オブジェクトをシリアライズした場合は、ObjectOutputStream.reset() を呼び出してシリアライゼーション キャッシュをクリアする必要があります。そうしないと、プロセスがメモリ リークを起こし、最終的に停止します。欠点は、非一時的なオブジェクトが再シリアル化されることです。一時的な結果オブジェクトをシリアライズすることは、あなたが思っているよりも少し面倒です!
ソフト参照の使用を検討してください。ソフト参照とは何かがわからない場合は、java.lang.ref.SoftReference の javadoc を読んでください。
本当に興奮しない限り、Phantom 参照と Weak 参照は避けてください。
最後に、GC を本当に許容できない場合は、Realtime Java を使用してください。
いいえ、冗談ではありません。
リファレンス実装は無料でダウンロードでき、SUN の Peter Dibbles の本は非常に読みやすいものです。
ファイナライザーに関する限り:
- それらは事実上役に立たない。それらはタイムリーに呼び出されることが保証されていないか、実際にはまったく呼び出されません (GC が実行されない場合、ファイナライザーも実行されません)。これは、通常、それらに依存するべきではないことを意味します。
- ファイナライザーはべき等であるとは限りません。
finalize()
ガベージ コレクターは、同じオブジェクトに対して 2 回以上呼び出されないように細心の注意を払っています。適切に作成されたオブジェクトでは問題になりませんが、不適切に作成されたオブジェクトでは、finalize を複数回呼び出すと問題が発生する可能性があります (たとえば、ネイティブ リソースの二重解放 ... クラッシュ)。 - メソッドを持つすべてのオブジェクトは、 (または同様の) メソッド
finalize()
も提供する必要があります。close()
これは、呼び出す必要がある関数です。例:FileInputStream.close()
。あなたが呼び出すことを意図しfinalize()
たより適切なメソッドがある場合、呼び出す理由はありません。
ファイナライズでOSハンドルを閉じる際の実際の問題は、ファイナライズが保証された順序で実行されないことです。しかし、ブロックするもの(ソケットなど)へのハンドルがある場合、コードがデッドロック状態になる可能性があります(些細なことではありません)。
だから私は、予測可能な順序でハンドルを明示的に閉じるためのものです。基本的に、リソースを処理するためのコードは、次のパターンに従う必要があります。
SomeStream s = null;
...
try{
s = openStream();
....
s.io();
...
} finally {
if (s != null) {
s.close();
s = null;
}
}
JNIを介して機能し、ハンドルを開く独自のクラスを作成すると、さらに複雑になります。ハンドルが閉じられている(解放されている)ことと、ハンドルが1回だけ発生することを確認する必要があります。デスクトップJ2SEで見落とされがちなOSハンドルはですGraphics[2D]
。BufferedImage.getGrpahics()
ビデオドライバーを指すハンドルを返す可能性もあります(実際にはGPUでリソースを保持しています)。自分でリリースせず、ガベージコレクターに任せて作業を行う場合は、ビデオカードにマップされたビットマップを使い果たしても十分なメモリがあると、奇妙なOutOfMemoryなどの状況が発生する可能性があります。私の経験では、グラフィックスオブジェクトを操作するタイトなループ(サムネイルの抽出、スケーリング、名前のシャープ化)でかなり頻繁に発生します。
基本的に、GCはプログラマーが正しいリソース管理を行う責任を負いません。それはメモリだけを処理し、他には何も処理しません。close()IMHOを呼び出すStream.finalizeは、例外new RuntimeError( "まだ開いているストリームを収集するガベージ")をスローするように実装する方が適切です。ずさんなアマチュアが終わりを失った後、それはコードのデバッグとクリーニングの時間と日を節約します。
ハッピーコーディング。
平和。
ファイナライザーが .NET の同名の名前に似ていると仮定すると、リークする可能性のあるファイル ハンドルなどのリソースがある場合にのみ、ファイナライザーを呼び出す必要があります。ほとんどの場合、オブジェクトにはこれらの参照がないため、呼び出す必要はありません。
自分のゴミじゃないからゴミを集めようとするのは良くない。オブジェクトを作成したときに VM にメモリを割り当てるように指示しましたが、ガベージ コレクターはそれらのオブジェクトに関する情報を隠しています。内部的に、GC はメモリ割り当ての最適化を実行しています。手動でガベージを収集しようとすると、GC が何を保持して削除したいのかがわからず、手で強制するだけです。その結果、内部計算が台無しになります。
GC が内部で保持しているものについて詳しく知っていれば、より多くの情報に基づいた決定を下すことができるかもしれませんが、そうすると GC の利点を逃してしまいます。
GC は、物事を適切にファイナライズするタイミングについて多くの最適化を行います。
そのため、GC が実際にどのように機能し、どのように世代をタグ付けするかを熟知していない限り、手動で finalize を呼び出したり、GC を開始したりすると、パフォーマンスが低下する可能性があります。
ファイナライザーは避けてください。タイムリーに呼び出されるという保証はありません。メモリ管理システム (つまり、ガベージ コレクター) がファイナライザーを使用してオブジェクトを収集することを決定するまでに、かなりの時間がかかる場合があります。
多くの人がファイナライザーを使用して、ソケット接続を閉じたり、一時ファイルを削除したりします。そうすることで、アプリケーションの動作が予測不能になり、JVM がオブジェクトを GC するタイミングに結び付けられます。これは、Java ヒープが使い果たされたことが原因ではなく、システムが特定のリソースのハンドルを使い果たしたことが原因で、「メモリ不足」のシナリオにつながる可能性があります。
System.gc() またはそのようなハンマーへの呼び出しを導入すると、環境で良い結果が得られる可能性がありますが、必ずしも他のシステムに変換されるとは限りません。誰もが同じ JVM を実行しているわけではありません。SUN、IBM J9、BEA JRockit、Harmony、OpenJDK など、多数あります。この JVM はすべて JCK (公式にテストされているもの) に準拠していますが、多くの高速化に関しては自由です。GC は、誰もが多額の投資を行っている分野の 1 つです。ハンマーを使用すると、多くの場合、その努力が台無しになります。