JVM
で finalize()
メソッドがいつ呼び出されるのかを知る必要があります。 finalize()
メソッドがオーバーライドされて呼び出されたときにファイルに書き込むテストクラスを作成しました。実行されません。誰もそれが実行されていない理由を私に言うことができますか?
一般に、クリーンアップなどを行うためにfinalize()
に頼らないことが最善です。
Javadoc (これは読む価値があるでしょう)によると、それは次のとおりです。
オブジェクトへの参照がもうないとガベージコレクションが判断したときに、オブジェクトに対してガベージコレクタによって呼び出されます。
Joachimが指摘したように、オブジェクトが常にアクセス可能であれば、これはプログラムの実行中には決して起こりません。
また、ガベージコレクタが特定の時間に実行される保証はありません。一般的に、私が言おうとしているのは、finalize()
は、あなたがそれを必要とする何か特別なものがない限り、おそらく一般的に使うのに最も良い方法ではないということです。
finalize
メソッドは、オブジェクトがガベージコレクトされようとしているときに呼び出されます。それはガベージコレクションの対象となった後はいつでも可能です。
オブジェクトがガベージコレクトされることは絶対にあり得ない(したがってfinalize
が呼び出されることは決してない)可能性は十分にあります。これは、オブジェクトがgcの対象とならない場合(JVMの有効期間全体を通して到達可能なため)、またはオブジェクトが対象となる時点からJVMの実行が停止されるまでの間にガベージコレクションが実際に実行されない場合に起こりますテストプログラム)。
まだ呼び出されていないオブジェクトに対してfinalize
を実行するようにJVMに指示する方法はありますが、それらを使用することもお勧めできません(その方法の保証もそれほど強力ではありません)。
アプリケーションの正しい操作をfinalize
に頼っているのであれば、何か問題があります。 finalize
は only (通常はJava以外の)リソースのクリーンアップに使用されるべきです。そしてそれは 正確に であるため、JVMはfinalize
がどのオブジェクトに対しても呼び出されることを保証しません。
protected void finalize() throws Throwable {}
- すべてのクラスはJava.lang.Objectから
finalize()
メソッドを継承します。- オブジェクトへの参照が存在しないと判断されたときにガベージコレクタによって呼び出される
- object finalizeメソッドはアクションを実行しませんが、任意のクラスによってオーバーライドされる可能性があります。
- 通常、Java以外のリソースをクリーンアップする、つまりファイルを閉じるためにオーバーライドされるべきです。
finalize()
をオーバーライドする場合は、try-catch-finally文を使用し、常にsuper.finalize()
を呼び出すことをお勧めします。これは、クラスを呼び出すオブジェクトによって使用されているリソースを誤って閉じてしまうのを防ぐための安全対策です。protected void finalize() throws Throwable { try { close(); // close open files } finally { super.finalize(); } }
ガベージコレクション中に
finalize()
がスローした例外はファイナライズを中止しますが、それ以外は無視されますfinalize()
はどのオブジェクトに対しても二度以上実行されることはありません
から引用: http://www.janeg.ca/scjp/gc/finalize.html
この記事をチェックすることもできます:
Javaのfinalize()
メソッドはデストラクタではないので、アプリケーションが依存するロジックを処理するために使用しないでください。 Javaの仕様では、アプリケーションのライブ期間中にfinalize
メソッドがまったく呼び出されるという保証はありません。
おそらく欲しいのは、以下のようにfinally
とcleanupメソッドの組み合わせです。
MyClass myObj;
try {
myObj = new MyClass();
// ...
} finally {
if (null != myObj) {
myObj.cleanup();
}
}
Effective Java、2nd edition page 27をチェックしてください。 Item 7:ファイナライザーを避けてください
ファイナライザは予測不可能で、しばしば危険で、そして一般的には不要です。 ファイナライザでタイムクリティカルなことは絶対にしないでください。重要な永続状態を更新するためにファイナライザに頼らないでください。
リソースを終了するには、代わりにtry-finallyを使用してください。
// try-finally block guarantees execution of termination method Foo foo = new Foo(...); try { // Do what must be done with foo ... } finally { foo.terminate(); // Explicit termination method }
Javaで
finalize()
メソッドが呼び出されるのはいつですか。
GCがオブジェクトにアクセスできなくなったことをGCが検出した後で、オブジェクトが使用していたメモリを実際に再利用する前に、finalizeメソッドが呼び出されます。
オブジェクトが到達不能になった場合、finalize()
は呼び出されません。
GCが動作しない場合は、finalize()
が呼び出されることはありません。 (通常、GCは、価値のあるものにするのに十分なゴミがあるとJVMが判断したときにのみ実行されます。)
特定のオブジェクトに到達できないとGCが判断するまでに、1 GCサイクル以上かかる場合があります。 (Java GCは通常「世代別」コレクターです...)
オブジェクトが到達不能でファイナライズ可能であることをGCが検出すると、ファイナライズキューに入れられます。ファイナライズは通常、通常のGCとは非同期に行われます。
(実際にはJVMの仕様は 許可 JVMが 決して ファイナライザーを実行する - オブジェクトが使用しているスペースを取り戻さないという条件で...しかし、この振る舞いは「許可されています」。
その結果、明確な期間内に行わなければならないことをファイナライズに頼るのは賢明ではありません。まったく使わないのが「ベストプラクティス」です。 finalize()
メソッドであなたがやろうとしていることなら何でもするためのもっと良い(すなわち、より信頼できる)方法があるはずです。
ファイナライズの唯一の合法的な用途は、アプリケーションコードによって失われたオブジェクトに関連したリソースをクリーンアップすることです。それでも、そもそもオブジェクトが失われないようにアプリケーションコードを書くようにしてください。 (例えば、close()
が常に呼ばれることを保証するためにJava 7+ try-with-resources を使用してください。)
これをオーバーライドしてfinalize()メソッドが呼び出されたときにファイルに書き込むテストクラスを作成しました。実行されません。誰もそれが実行されていない理由を私に言うことができますか?
言うのは難しいですが、いくつかの可能性があります。
JVMによるfinalize()メソッドの呼び出しには不確実性があるため(オーバーライドされたfinalize()が実行されるかどうかわからない)、研究目的のためには、finalize()が呼び出されたときに何が起こるかを観察するよりよい方法はJVMにコマンドSystem.gc()
によるガベージコレクションの呼び出しを強制します。
具体的には、オブジェクトが使用されなくなったときにfinalize()が呼び出されます。しかし、新しいオブジェクトを作成してそれを呼び出そうとしたとき、その呼び出しの確実性はありません。そのため、確実にするために、null
オブジェクトc
を作成します。これは明らかに将来の用途はありません。したがって、c
のfinalize呼び出しが表示されます。
例
class Car {
int maxspeed;
Car() {
maxspeed = 70;
}
protected void finalize() {
// Originally finalize method does nothing, but here we override finalize() saying it to print some stmt
// Calling of finalize is uncertain. Difficult to observe so we force JVM to call it by System.gc(); GarbageCollection
System.out.println("Called finalize method in class Car...");
}
}
class Bike {
int maxspeed;
Bike() {
maxspeed = 50;
}
protected void finalize() {
System.out.println("Called finalize method in class Bike...");
}
}
class Example {
public static void main(String args[]) {
Car c = new Car();
c = null; // if c weren`t null JVM wouldn't be certain it's cleared or not, null means has no future use or no longer in use hence clears it
Bike b = new Bike();
System.gc(); // should clear c, but not b
for (b.maxspeed = 1; b.maxspeed <= 70; b.maxspeed++) {
System.out.print("\t" + b.maxspeed);
if (b.maxspeed > 50) {
System.out.println("Over Speed. Pls slow down.");
}
}
}
}
出力
Called finalize method in class Car...
1 2 3 4 5 6 7 8 9
10 11 12 13 14 15 16 17 18 19
20 21 22 23 24 25 26 27 28 29
30 31 32 33 34 35 36 37 38 39
40 41 42 43 44 45 46 47 48 49
50 51Over Speed. Pls slow down.
52Over Speed. Pls slow down.
53Over Speed. Pls slow down.
54Over Speed. Pls slow down.
55Over Speed. Pls slow down.
56Over Speed. Pls slow down.
57Over Speed. Pls slow down.
58Over Speed. Pls slow down.
59Over Speed. Pls slow down.
60Over Speed. Pls slow down.
61Over Speed. Pls slow down.
62Over Speed. Pls slow down.
63Over Speed. Pls slow down.
64Over Speed. Pls slow down.
65Over Speed. Pls slow down.
66Over Speed. Pls slow down.
67Over Speed. Pls slow down.
68Over Speed. Pls slow down.
69Over Speed. Pls slow down.
70Over Speed. Pls slow down.
注 - 70まで印刷した後で、オブジェクトbがプログラムで使用されていない場合でも、 "クラスBike ...のCalled finalizeメソッド..."が印刷されないため、JVMによってbがクリアされるかどうかは不明です。 。
finalizeはクラス作成の件数を表示します。
protected void finalize() throws Throwable {
System.out.println("Run F" );
if ( checkedOut)
System.out.println("Error: Checked out");
System.out.println("Class Create Count: " + classCreate);
}
メイン
while ( true) {
Book novel=new Book(true);
//System.out.println(novel.checkedOut);
//Runtime.getRuntime().runFinalization();
novel.checkIn();
new Book(true);
//System.runFinalization();
System.gc();
ご覧のように。次の出力は、クラス数が36のときに最初に実行されたgcを示しています。
C:\javaCode\firstClass>Java TerminationCondition
Run F
Error: Checked out
Class Create Count: 36
Run F
Error: Checked out
Class Create Count: 48
Run F
最近(テスト中に接続プールを破棄するために)ファイナライザーメソッドに取り組んできたので、ファイナライザーには多くのことが欠けていると私は言わなければなりません。実際の対話を追跡するために弱い参照を使用するのと同様にVisualVMを使用して観察しました。Java8環境では、次のことが当てはまることがわかりました(Oracle JDK、Ubuntu 15)。
最終的な考え
ファイナライズメソッドは信頼できませんが、1つの目的にしか使用できません。オブジェクトがガベージコレクションされる前に閉じられたり破棄されたりすることを保証することができます。これにより、ライフサイクル終了アクションを含むより複雑なライフサイクルを持つオブジェクトが正しく処理される場合、フェイルセーフを実装できます。それが私が考えることができる1つの理由はそれを無効にするためにそれを価値があるようにします。
時にはそれが破壊された時、物は行動を起こさなければなりません。たとえば、オブジェクトにファイルハンドルやフォントなどのJava以外のリソースがある場合は、オブジェクトを破棄する前にこれらのリソースが解放されていることを確認できます。このような状況を管理するために、Javaは "ファイナライズ"と呼ばれるメカニズムを提供しています。ファイナライズすることで、オブジェクトがガベージコレクタから削除されようとしているときに発生する特定のアクションを定義できます。クラスにファイナライザを追加するには、 finalize() メソッドを定義するだけです。 Java実行時は、そのクラスのオブジェクトを削除しようとしているときはいつでもこのメソッドを呼び出します。 finalize method() の中で、オブジェクトを破棄する前に実行するアクションを指定します。ガベージコレクタは、どの実行状態も参照しなくなったオブジェクト、または間接的に参照を持つ他のオブジェクトを参照しないオブジェクトを定期的に検索します。アセットが解放される前に、Javaランタイムはオブジェクトに対して finalize() メソッドを呼び出します。 finalize() メソッドの一般形式は次のとおりです。
protected void finalize(){
// This is where the finalization code is entered
}
protected キーワードを使用すると、そのクラス外のコードによる finalize() へのアクセスが防止されます。 finalize() はガベージコレクションの直前に呼び出されることを理解することが重要です。たとえば、オブジェクトがスコープを離れたときには呼び出されません。それは、いつ、または finalize() が実行されるのかがわからないことを意味します。結果として、プログラムは、システム資源またはオブジェクトによって使用されている他の資源を解放するための他の手段を提供しなければなりません。 プログラムの通常の実行では、 finalize() に頼るべきではありません。
オブジェクトがライブスレッドや静的参照から到達できない場合、そのオブジェクトはガベージコレクションまたはGCの対象になります。つまり、オブジェクトの参照がすべてnullの場合、そのオブジェクトはガベージコレクションの対象になります。循環依存関係は参照としてカウントされないため、オブジェクトAにオブジェクトBの参照があり、オブジェクトBにオブジェクトAの参照があり、他にライブ参照がない場合、オブジェクトAとBの両方がガベージコレクションの対象になります。一般に、オブジェクトは以下の場合にJavaのガベージコレクションの対象になります。
finalize methodは保証されていません。このメソッドは、オブジェクトがGCに適格になったときに呼び出されます。オブジェクトがガベージコレクションされない可能性がある多くの状況があります。
Finalizeメソッドをオーバーライドするクラス
public class TestClass {
public TestClass() {
System.out.println("constructor");
}
public void display() {
System.out.println("display");
}
@Override
public void finalize() {
System.out.println("destructor");
}
}
Finalizeメソッドが呼び出される可能性
public class TestGarbageCollection {
public static void main(String[] args) {
while (true) {
TestClass s = new TestClass();
s.display();
System.gc();
}
}
}
メモリがダンプオブジェクトでオーバーロードされると、gcはfinalizeメソッドを呼び出します。
あなたが頻繁に呼び出されているfinalizeメソッドを見つけることができないコンソールを実行して見てください。
finalize()
はガベージコレクションの直前に呼び出されます。オブジェクトが範囲外になったときには呼び出されません。これは、finalize()
がいつ実行されるのか、あるいはいつ実行されるのかさえわからないことを意味します。
例:
プログラムがガベージコレクタが発生する前に終了すると、finalize()
は実行されません。したがって、プログラムが通常の操作で使用する手段としてではなく、他のリソースを適切に処理するためのバックアップ手順として、または特殊用途のアプリケーションとして使用する必要があります。
Javaはオブジェクトが呼ばれるかもしれないfinalize()と呼ばれるメソッドを実装することを可能にします。
ガベージコレクタがオブジェクトを収集しようとすると、finalize()メソッドが呼び出されます。
ガベージコレクタが実行されない場合、メソッドは呼び出されません。
ガベージコレクタがオブジェクトの収集に失敗して もう一度実行しようとすると、メソッドは2回目に呼び出されません。
実際には、実際のプロジェクトでこれを使用することはほとんどありません。
それが呼ばれないかもしれないこと、そしてそれが間違いなく二度呼ばれることはないということを心に留めておいてください。 finalize()メソッドは0回または1回実行できます。
次のコードでは、ガベージコレクタを実行する必要がある前にプログラムが終了するため、finalize()メソッドを実行しても出力は生成されません。