15

Bruce Eckel のThinking in Java 4th editionにゆっくりと取り組んでいますが、次の問題に悩まされています。

メッセージを出力する finalize( ) メソッドを持つクラスを作成します。main( ) で、クラスのオブジェクトを作成します。finalize( ) が常に呼び出されるように、前の演習を変更します。

これは私がコーディングしたものです:

public class Horse {
    boolean inStable;
    Horse(boolean in){
        inStable = in;
    }   
    public void finalize(){
        if (!inStable) System.out.print("Error: A horse is out of its stable!");
    }
}
public class MainWindow {
    public static void main(String[] args) {
        Horse h = new Horse(false);
        h = new Horse(true);
        System.gc();
    }
}

ブール値が に設定された新しいHorseオブジェクトを作成します。ここで、メソッド内で が であるかどうかを確認します。そうであれば、メッセージを出力します。inStablefalsefinalize()inStablefalse

残念ながら、メッセージは印刷されません。条件が に評価されるので、そもそも呼び出されていないとtrue思います。finalize()プログラムを何度も実行しましたが、エラー メッセージが表示されるのは数回だけでした。System.gc()が呼び出されると、ガベージ コレクターは参照されていないオブジェクトを収集するという印象を受けました。

正解をグーグルで検索すると、このリンクが表示されました。これにより、より詳細で複雑なコードが得られます。、 、などSystem.runFinalization()、これまでに見たことのないメソッドを使用します。Runtime.getRuntime()System.runFinalizersOnExit()

どのように機能するか、強制的に実行する方法をよりよく理解できる人はfinalize()いますか? または、ソリューション コードで行われていることを順を追って説明してくれますか?

4

5 に答える 5

17

ガベージコレクターがコレクションに適格であるが、finalizerそれを持っているオブジェクトを見つけたとき、それはすぐに割り当てを解除しません。ガベージコレクターは可能な限り迅速に完了しようとするため、ファイナライザーが保留中のオブジェクトのリストにオブジェクトを追加するだけです。ファイナライザーは、後で別のスレッドで呼び出されます。

System.runFinalizationガベージコレクションの後でメソッドを呼び出すことにより、保留中のファイナライザーをすぐに実行しようとするようにシステムに指示できます。

ただし、ファイナライザーを強制的に実行する場合は、自分で呼び出す必要があります。ガベージコレクターは、オブジェクトが収集されること、またはファイナライザーが呼び出されることを保証しません。それは「最善の努力」をするだけです。ただし、ファイナライザーを実際のコードで実行するように強制する必要があることはめったにありません。

于 2012-08-19T23:03:56.243 に答える
1

おもちゃのシナリオ以外ではfinalize、ガベージ コレクターはどの参照が「意味のある」かを知る方法がないため、「意味のある」参照が存在しないオブジェクトに対して a が常に呼び出されることを保証することは通常不可能です。たとえば、 のArrayListようなオブジェクトには、そのカウントをゼロに設定し、バッキング配列内のすべての要素を将来の呼び出しで上書きできるようにする「クリア」メソッドがある場合がありますがAdd、実際にはそのバッキング配列内の要素をクリアしません。オブジェクトにサイズ 50 の配列があり、その配列Countが 23 の場合、コードが配列の最後の 27 スロットに格納された参照を調べることができる実行パスは存在しない可能性がありますが、ガベージの方法はありません。それを知るコレクター。その結果、finalizeコンテナーがそれらの配列スロットを上書きするか、コンテナーが配列を放棄するか (おそらくより小さい配列を優先して)、またはコンテナー自体へのすべてのルート化された参照が破棄されるか、存在しなくなるまで、またはそれらのスロット内のオブジェクトに対して。

システムの呼び出しを促進するさまざまな手段があります。finalize強力なルート参照が存在しないオブジェクト (これが質問のポイントであり、他の回答が既にカバーされているようです)。参照が存在し、コードが関心を持つ可能性のあるオブジェクトのセットです。2 つのセットは大部分が重複していますが、各セットには他のオブジェクトに含まれていないオブジェクトを含めることができます。オブジェクトのファイナライザー` は、ファイナライザーが存在する場合を除き、オブジェクトが存在しないと GC が判断したときに実行されます。それはタイムコードと一致するかもしれないし、一致しないかもしれません。関心がなくなったすべてのオブジェクトに対してファイナライザを実行できると便利ですが、一般的には不可能です。

于 2013-06-13T16:16:27.233 に答える
0

garabage collecter ( System.gc()) メソッドの呼び出し、Java 仮想マシンが未使用のオブジェクトをリサイクルして、現在占有しているメモリをすばやく再利用できるようにすることを示唆しています (つまり、jvm への単なる提案であり、実行するためにバインドしません)。アクションはその場で、同じことをする場合としない場合があります)。メソッド呼び出しから制御が戻ると、Java 仮想マシンは、破棄されたすべてのオブジェクトからスペースを再利用するために最善を尽くしています。finalize() は、オブジェクトへの参照がなくなったとガベージ コレクションが判断したときに、オブジェクトのガベージ コレクタによって呼び出されます。

于 2012-08-19T23:33:00.957 に答える
0

これが私にとってうまくいったことです(部分的にですが、アイデアを説明しています):

class OLoad {

    public void finalize() {
        System.out.println("I'm melting!");
    }
}

public class TempClass {

    public static void main(String[] args) {
        new OLoad();
        System.gc();
    }
}

new OLoad(); 参照が添付されていないオブジェクトを作成するので、トリックを行います。これにより、System.gc()は参照のないオブジェクトを検出するため、finalize() メソッドを実行できます。OLoad o1 = new OLoad();のようなことを言います。main() の最後まで存在する参照を作成するため、機能しません。残念ながら、これはほとんどの場合うまくいきます。他の人が指摘したように、自分で呼び出す以外に、finalize()が常に呼び出されるようにする方法はありません。

于 2013-06-11T03:19:23.413 に答える