7

リソースをクリーンアップするためのclose()メソッドを使用してリソースクラスを作成し、誰かがclose()の呼び出しを忘れた場合に、finalize()をオーバーライドしてリソースを解放(および警告を出力)したいとします。どうすればこれを適切に行うことができますか?

  • ネイティブ(JNI割り当て)リソースにのみ推奨されますか?
  • ファイナライザーから、ファイナライズされた別のオブジェクトへの参照を使用するとどうなりますか?循環依存関係がある場合、ガベージコレクターが、ファイナライザーが実行された可能性のあるオブジェクトへのアクセスをどのように防ぐことができるかわかりません。
  • リソースリークを検出および/または処理するためにfinalize()をオーバーライドするためのより良い代替手段はありますか?
  • ファイナライザーを実装するときに注意すべき他の落とし穴はありますか?

注:finalize()を使用することは通常悪い考えであり、呼び出されることが保証されていないことを私は知っています。これについて議論する他のいくつかの質問があります。この質問は、Javaでファイナライザーを実装する方法に関するものであり、なぜすべきか(またはすべきでないか)についてではありません。

4

2 に答える 2

6

効果的なjava(第2版)では、Joshuaがアイテム#7でこれを行う方法について詳しく説明します。彼は最初に、sを使用することはほとんどないことを提案しますfinalizer。ただし、リソースリークがあることを示すログステートメントのみを出力するために使用する理由の1つ。彼は、この方法でそれを行うことの欠点の1つは、誰かがあなたのクラスを拡張し、スーパーファイナライザーを適切に呼び出さない可能性があることだと言います。したがって、彼はサブクラスで次のようなことを行うことを提案します。

// Manual finalizer chaining
   @Override protected void finalize() throws Throwable {
       try {
           ... // Finalize subclass state
       } finally {
           super.finalize();
   } 
}

これは、現在のクラスで何かが壊れた場合finallyでも、が呼び出されるようにするためです。これは、クラスをサブクラス化する人によって異なるため、おそらく悪い解決策です。別の解決策は、保護者オブジェクトファイルを使用してこれを実行することです。これは次のようになります。

// Finalizer Guardian idiom
   public class Foo {
// Sole purpose of this object is to finalize outer Foo object
      private final Object finalizerGuardian = new Object() {
         @Override protected void finalize() throws Throwable {
            ... // Finalize outer Foo object
         }
      };
      ...  // Remainder omitted
}

誰もその機能を上書きできないことがわかっているので、これはよりクリーンなアプローチです。

リソースを閉じるための推奨される方法はまだ実装中Closeableであり、閉じるのはユーザー次第であることを確認してください。Josuhaが示唆しているように、このメソッドでは時間に敏感な操作を実行しないでくださいfinalize。JVMは、将来的にそれを実行することを選択する可能性があります。コミットまたは重要なことを行うためにこのメソッドに依存している場合、それは悪い考えです。

于 2012-09-17T15:23:54.673 に答える
1

ネイティブ(JNI割り当て)リソースにのみ推奨されますか?

いいえ。あなたのユースケースはファイナライザーにとっても有効なものです。ロギングリソースリークを意味します。

ファイナライザーで、ファイナライズされた別のJavaオブジェクトにアクセスするとどうなりますか?

それでもアクセスできる場合は、確定されていません。または多分私はあなたの質問に何かが欠けています。

今、私は分かる。AとBの両方がガベージコレクションの対象であり、それらの間に参照がある場合があります。デフォルトでは何もしないので問題finalize()ありません。両方のオブジェクトのカスタムfinalize()メソッドを作成する場合は、ファイナライズされる順序に関係なくコードを作成する必要があります。null対応するオブジェクトがすでにガベージコレクションされている可能性があるため、参照がになることにも注意する必要があります。

リソースリークを検出および/または処理するためにfinalize()をオーバーライドするためのより良い代替手段はありますか?

ファイナライザーを使用する際に最も重要なことは、リークを検出してログに記録し、リークを処理していないことを警告することだと思います。そして、このリークをログに記録するのに最適なのは、リソースオブジェクトがガベージコレクションされる前です。したがって、ファイナライザーはこれに自然に適合します。

これを強調したいと思います。リソースを閉じるのを忘れたプログラマーに対処するためにファイナライザーを使用するのではなく、コードを修正する必要があることを彼に通知します。

ファイナライザーを実装するときに注意すべき他の落とし穴はありますか?

ファイナライザーを持つオブジェクトにはパフォーマンスの低下もあるようです。テストを行って、それがどのように機能するかを確認する必要があります。重要なパフォーマンスの低下がある場合は、開発時にのみファイナライザーを使用してリソースリークをログに記録するメカニズムを想像してみます。

また、Amir Raminfarが提案したように、ファイナライザーを使用してオブジェクトを継承する場合は注意が必要です。

FileInputStream][1]もう1つ、[のソースコードを見てください[FileOutputStream][2]。同じ理由でファイナライザーを使用しています。

于 2012-09-17T15:51:18.927 に答える