ファイナライズについての私の理解はこれです:
オブジェクトが占有するメモリをクリーンアップまたは再利用するために、ガベージコレクタが機能します。(自動的に呼び出されますか?)
次に、ガベージコレクターはオブジェクトを逆参照します。ガベージコレクタがオブジェクトにアクセスする方法がない場合があります。次に、finalizeが呼び出されて最終クリーンアップ処理が実行され、その後ガベージコレクターが呼び出されます。
これはファイナライズの正確な説明ですか?
ファイナライズについての私の理解はこれです:
オブジェクトが占有するメモリをクリーンアップまたは再利用するために、ガベージコレクタが機能します。(自動的に呼び出されますか?)
次に、ガベージコレクターはオブジェクトを逆参照します。ガベージコレクタがオブジェクトにアクセスする方法がない場合があります。次に、finalizeが呼び出されて最終クリーンアップ処理が実行され、その後ガベージコレクターが呼び出されます。
これはファイナライズの正確な説明ですか?
ガベージコレクターはバックグラウンドで自動的に機能しています(明示的に呼び出すことはできますが、これが必要になることはまれです)。基本的に、他のオブジェクトによって参照されていないオブジェクトのみをクリーンアップします(もちろん、全体像はより複雑ですが、これが基本的な考え方です)。したがって、ライブオブジェクトの参照は変更されません。ライブオブジェクトからオブジェクトにアクセスできない場合、これは安全にガベージコレクションできることを意味します。
ファイナライズは、オブジェクトによって取得されたリソース(メモリではなく、ファイルハンドル、ポート、DB接続などの他のリソース)をクリーンアップすることを目的としていました。しかし、それは実際にはうまくいきませんでした:-(
finalize()
いつ呼び出されるかは予測できませんfinalize()
これまでに呼び出される保証はありません。したがって、呼び出されることが保証されていても、リソースを解放するのに適した場所ではありません。開いているすべてのDB接続を解放するために呼び出されるまでに、システムは解放された接続を完全に使い果たしている可能性があります。アプリが機能しなくなりました。
この記事から:
finalize() メソッドを実装するクラスのインスタンスは、多くの場合、ファイナライズ可能なオブジェクトと呼ばれます。それらが参照されなくなっても、Java ガベージ コレクタによってすぐに回収されることはありません。代わりに、Java ガベージ コレクターはオブジェクトをファイナライズ プロセス用の特別なキューに追加します。通常、一部の Java 仮想マシンでは「参照ハンドラ」と呼ばれる特別なスレッドによって実行されます。このファイナライズ プロセス中に、「Finalizer」スレッドはオブジェクトの各 finalize() メソッドを実行します。finalize() メソッドが正常に完了した後でのみ、オブジェクトは Java ガベージ コレクションに引き渡され、「将来の」ガベージ コレクションによってその領域が再利用されます。
クラスの finalize() メソッドでは、実質的に何でも自由に行うことができます。その場合、オブジェクトが参照されなくなったり不要になったりしたときに、すべてのオブジェクトが占有していたメモリ空間が Java ガベージ コレクタによって再利用されるとは思わないでください。なんで?finalize() メソッドがタイムリーに実行を完了することは保証されていません。最悪の場合、オブジェクトへの参照がなくなっても呼び出されない可能性があります。つまり、 finalize() メソッドを持つオブジェクトがガベージ コレクションされるとは限りません。
また、 Sun のこの記事には、プロセスを説明するいくつかの優れた図があります。
いいえ。このfinalize()
メソッドは、ガベージコレクターがオブジェクトを再利用しようとした場合にのみ実行されます。
オブジェクトが使用するメモリはすべて(通常、例外は考えられませんが)自動的にオブジェクトに接続され、オブジェクトとともにクリーンアップされます。したがって、ファイナライズはメモリを解放するためのものではなく、オブジェクトが関連付けられている可能性のある他のリソースを解放するためのものです。たとえば、これを使用して、開いているファイルやデータベース接続を閉じたり、オペレーティングシステムとインターフェイスする低レベルのコードを実行して、システムレベルのリソースを解放したりできます。
実際、 finalize() メソッドの動作は次のとおりです。
ガベージ コレクターが実行されると (VM はメモリを解放する必要があると判断したため、強制的に実行することはできません)、このオブジェクトからメモリを収集することを決定しました (つまり、少なくとも到達可能なオブジェクトから、それを指す参照がもう存在しないことを意味します)。 、占有されているメモリを削除する直前に、オブジェクトに対してメソッド finalize() を実行します。ガベージ コレクションが行われると、オブジェクトが消える直前に finalize() が実行されることは確かですが、GC されるとは確信できないため、サニタイズを行うためにメソッドに依存するべきではありません。 . 最終的に {} ブロック内でサニタイズ ステートメントを実行し、実行が保証されていない finalize() を使用しないでください。
さらに、何人かの人々がパフォーマンス テストを行い、ファイナライズ メソッドがオブジェクトの作成/破棄をいくらか遅くすることを示しました。ソースを思い出せないので、この情報はあまり信頼できないものとして扱ってください. :)
ファイナライズは、ガベージ コレクターによって解放できないリソースをクリーンアップするために使用されます。たとえばnative
、OS から (いくつかの API を介して) リソースを直接割り当てるプログラムを考えてみましょう。これは通常、ある種の「ハンドル」 (UNIX ファイル記述子または Windows HANDLE、または同様のもの) を生成します。
class Wrapper {
private long handle;
private Handle(long h) {
handle = h;
}
private static native long getHandleFromOS();
static Wrapper allocate() {
return new Handle(getHandleFromOS());
}
}
では、コードが class のインスタンスを割り当てるとどうなるWrapper
でしょうか? クラスはある種の OS 固有のリソースを割り当て、それへの参照 (ハンドル) をメンバー変数に保持します。しかし、ラッパー インスタンスへの最後の Java 参照が失われた場合はどうなるでしょうか。これで、ガベージ コレクターは (ある時点で) 現在機能していないラッパー インスタンスのスペースを再利用します。しかし、ラッパーによって割り当てられた OS リソースはどうなるでしょうか? ファイル記述子などのコストのかかるリソースである場合、上記のシナリオではリークされますが、これは悪いことです。
このようなシナリオでコードをクリーンアップできるようにするために、finalize
メソッドがあります。
class Wrapper {
private long handle;
private Handle(long h) {
handle = h;
}
protected void finalize() {
returnHandleToOS(handle);
}
private static native long getHandleFromOS();
private static native void returnHandleToOS(long handle);
static Wrapper allocate() {
return new Handle(getHandleFromOS());
}
}
これで、GC がラッパー インスタンスのスペースを再利用するときに、ファイナライザーは、リソースが OS に適切に返されることを確認します。
これはすべていいことのように聞こえますが、他の人がすでに指摘しているように、ファイナライズは本質的に信頼できないという欠点があります。ファイナライザーがいつ実行されるかはわかりません。さらに悪いことに、それが実行されるという保証はまったくありません。dispose
したがって、クラスのクライアントが参照を適切に破棄するのを忘れた場合に備えて、メカニズムを提供し、ファイナライズをセーフティネットとしてのみ使用するのが最善です。
class Wrapper {
private long handle;
private Handle(long h) {
handle = h;
}
protected void finalize() {
if( handle != 0 ) returnHandleToOS(handle);
}
public void dispose() {
returnHandleToOS(handle);
handle = 0;
}
private static native long getHandleFromOS();
private static native void returnHandleToOS(long handle);
static Wrapper allocate() {
return new Handle(getHandleFromOS());
}
}