web-dev-qa-db-ja.com

Java用のデストラクタはありますか?

Java用のデストラクタはありますか?私はこれに関するドキュメントを見つけることができないようです。ない場合は、どうすれば同じ効果が得られますか?

私の質問をより具体的にするために、私はデータを扱うアプリケーションを書いています、そして仕様はアプリケーションを元のちょうど起動された状態に戻す「リセット」ボタンがあるべきであると言います。ただし、アプリケーションを閉じるかリセットボタンを押さない限り、すべてのデータを「ライブ」にする必要があります。

通常はC/C++プログラマなので、これを実装するのは簡単なことだと思いました。リセットボタンが押されたときにすべての「ライブ」オブジェクトを破棄できるように、すべての「リセット可能」オブジェクトが同じクラスになるようにプログラムを構成しました。

ユーザーがデータを繰り返し入力してリセットボタンを押しただけでも、データの参照が解除され、ガベージコレクタがそれらを収集するのを待つだけでよいのではないかと考えていました。 Javaは言語としてはかなり成熟しているので、私はまた考えていました、これが起こるのを防ぐか、またはこれに優雅に取り組む方法があるべきです。

542
wai

Javaはガベージコレクション言語であるため、オブジェクトがいつ破棄されるのか(またはその場合でも)予測できません。したがって、デストラクタに直接相当するものはありません。

finalizeと呼ばれる継承されたメソッドがありますが、これは完全にガベージコレクタの判断で呼び出されます。そのため、明示的に整理する必要があるクラスでは、 close メソッドを定義し、健全性チェックのためだけにfinalizeを使用します(つまり、 close が呼び出されていない場合はログに記録します)。エラー)。

ファイナライズの詳細な議論を生み出した質問 最近、それが必要ならもっと多くの深さを提供するべきです...

486
Garth Gilmour

Java 7を使用している場合は、 try-with-resources ステートメントを見てください。例えば:

try (BufferedReader br = new BufferedReader(new FileReader(path))) {
  System.out.println(br.readLine());
} catch (Exception e) {
  ...
} finally {
  ...
}

ここで、不要になったリソースはBufferedReader.close()メソッドで解放されます。 AutoCloseableを実装する独自のクラスを作成し、それを同じように使用することができます。

この文はコードの構造化の点でfinalizeよりも制限されていますが、同時にコードの理解と保守が簡単になります。また、アプリケーションの有効期間中にfinalizeメソッドが呼び出されるという保証もありません。

111

いいえ、デストラクタはここにありません。その理由は、すべてのJavaオブジェクトがヒープ割り当てされ、ガベージコレクションされるためです。明示的な割り当て解除(つまりC++のdelete演算子)がなければ、実際のデストラクタを実装するための実用的な方法はありません。

Javaはファイナライザをサポートしていますが、ソケット、ファイルハンドル、ウィンドウハンドルなどのネイティブリソースへのハンドルを保持しているオブジェクトの保護手段としてのみ使用されることを意図しています。無料の地域、それだけです。オブジェクトにファイナライザがあるとき、それは最初に一時的な場所にコピーされ(ここではガベージコレクションです)、次にファイナライズ待ちのキューに入れられ、次にファイナライザスレッドが非常に低い優先度でキューをポーリングしますそしてファイナライザを実行します。

アプリケーションが終了すると、保留中のオブジェクトがファイナライズされるのを待たずにJVMが停止するため、ファイナライザが実行されるという保証は事実上ありません。

107
ddimitrov

finalize() メソッドの使用は避けるべきです。それらは、リソースをクリーンアップするための信頼できるメカニズムではありません。それらを悪用することによって、ガベージコレクタに問題を引き起こす可能性があります。

リソースの解放など、オブジェクトで割り当て解除呼び出しが必要な場合は、明示的なメソッド呼び出しを使用してください。この規約は既存のAPI(例: CloseableGraphics.dispose()Widget.dispose() )に見られ、通常try/finallyを介して呼ばれます。

Resource r = new Resource();
try {
    //work
} finally {
    r.dispose();
}

破棄されたオブジェクトを使用しようとすると、ランタイム例外がスローされます( IllegalStateException を参照)。


編集:

ユーザーがデータを繰り返し入力してリセットボタンを押しただけで、データの参照が解除され、ガベージコレクタが収集されるのを待つだけでよいのであれば、私は考えていましたか?

一般的に、あなたがする必要があるのはオブジェクトを間接参照することです - 少なくとも、これはそれが機能すると思われる方法です。ガベージコレクションが心配な場合は、 Java SE 6 HotSpot [tm]仮想マシンガベージコレクションチューニング (またはご使用のJVMバージョンの同等の文書)を参照してください。

25
McDowell

Java 1.7がリリースされたため、try-with-resourcesブロックを使用するという追加の選択肢があります。例えば、

public class Closeable implements AutoCloseable {
    @Override
    public void close() {
        System.out.println("closing..."); 
    }
    public static void main(String[] args) {
        try (Closeable c = new Closeable()) {
            System.out.println("trying..."); 
            throw new Exception("throwing..."); 
        }
        catch (Exception e) {
            System.out.println("catching..."); 
        }
        finally {
            System.out.println("finalizing..."); 
        } 
    }
}

このクラスを実行すると、c.close()は、tryブロックが終了したとき、およびcatchおよびfinallyブロックが実行される前に実行されます。 finalize()メソッドの場合とは異なり、close()は確実に実行されます。ただし、finally節で明示的に実行する必要はありません。

22
waku

私は他の答えに完全に同意し、ファイナライズの実行に頼らないようにします。

Try-catch-finallyブロックに加えて、 Runtime#addShutdownHook (Java 1.6で導入された)を使ってプログラムの最後のクリーンアップを実行することができます。

デストラクタが と同じではありませんが、クリーンアップメソッド(持続的データベース接続のクローズ、ファイルロックの削除など)を呼び出すことができるリスナオブジェクトを登録したシャットダウンフックを実装することができます。その は通常デストラクタ で行われます。繰り返しますが、これはコンストラクタの代わりにはなりませんが、場合によっては、これで必要な機能にアプローチできます。

これの利点は、プログラムの他の部分との間で、 疎結合された の構成解除動作があることです。

13
Markus Lux

いいえ、 Java.lang.Object#finalize が一番近いです。

しかし、いつ(そしてif)それが呼ばれても、保証されません。
参照: Java.lang.Runtime#runFinalizersOnExit(boolean)

9
flicken

まず、Javaはガベージコレクションされているので、オブジェクトの破壊に関して何かする必要があることは稀です。第一にあなたが解放するための管理リソースを通常持っていないため、そしてそれがいつ起こるのか、またはいつ起こるのか予測できないため、「誰も私のオブジェクトを使用しなくなるとすぐに」 ".

オブジェクトがJava.lang.ref.PhantomReferenceを使用して破棄された後に通知を受けることができます(実際には、破棄されたと言っても少し不正確な場合がありますが、それへのファントム参照がキューに入れられた場合同じこと)。一般的な使い方は次のとおりです。

  • 破壊する必要のあるリソースを別のヘルパーオブジェクトに分離します(接続を閉じることだけが行われている場合は、新しいクラスを作成する必要はありません)。閉じられる接続はその場合の "ヘルパーオブジェクト"になります)。
  • メインオブジェクトを作成するときには、それに対するPhantomReferenceも作成します。これに新しいヘルパーオブジェクトを参照させるか、PhantomReferenceオブジェクトから対応するヘルパーオブジェクトへのマップを設定します。
  • メインオブジェクトが集められた後、PhantomReferenceはキューに入れられます(あるいはそれはキューに入れられるかもしれません - ファイナライザのように、例えばVMが終了しても待たないという保証はありません)。あなたがそのキューを処理していることを確認してください(特別なスレッドで、または時々)。ヘルパーオブジェクトへのハードリファレンスのため、ヘルパーオブジェクトはまだ収集されていません。そのため、ヘルパーオブジェクトをクリーンアップしてからPhantomReferenceを破棄すると、ヘルパーオブジェクトも収集されます。

Finalize()もあります。これはデストラクタのように見えますが、動作はしません。通常は良い方法ではありません。

6
Steve Jessop

私はほとんどの答えに同意します。

finalizeまたはShutdownHookのどちらかに完全に頼ってはいけません

ファイナライズ

  1. JVMは、このfinalize()メソッドがいつ呼び出されるかを保証しません。

  2. オブジェクトがfinalizeメソッドから復活した場合、finalize()はGCスレッドによって一度だけ呼び出されますが、finalizeは再度呼び出されることはありません。

  3. あなたのアプリケーションでは、ガベージコレクションが決して呼び出されないライブオブジェクトがあるかもしれません。

  4. ファイナライズメソッドによってスローされた例外は、GCスレッドでは無視されます。

  5. System.runFinalization(true)メソッドとRuntime.getRuntime().runFinalization(true)メソッドはfinalize()メソッドを呼び出す可能性を高めますが、現在ではこれら2つのメソッドは推奨されなくなりました。これらの方法は、スレッドの安全性が欠けており、デッドロックが発生する可能性があるため、非常に危険です。

shutdownHooks

public void addShutdownHook(Thread hook)

新しい仮想マシンシャットダウンフックを登録します。

Java仮想マシンは、2種類のイベントに応答してシャットダウンします。

  1. 最後の非デーモンスレッドが終了したとき、またはexit(System.exitと同じ)メソッドが呼び出されたときに、プログラムは正常に終了します。
  2. 仮想マシンは、^ Cの入力などのユーザー割り込み、またはユーザーログオフやシステムシャットダウンなどのシステム全体のイベントに応答して終了します。
  3. シャットダウンフックは、単に初期化されているが起動されていないスレッドです。仮想マシンがシャットダウンシーケンスを開始すると、登録されているすべてのシャットダウンフックが不特定の順序で開始され、同時に実行されます。すべてのフックが終了したら、終了時の終了処理が有効になっていれば、呼び出されていないすべてのファイナライザを実行します。
  4. 最後に、仮想マシンは停止します。シャットダウンがexitメソッドの呼び出しによって開始された場合は、デーモンスレッドはシャットダウンシーケンスの間も実行されます。
  5. シャットダウンフックもすぐに作業を終了するはずです。プログラムがexitを呼び出すと、仮想マシンは即座にシャットダウンして終了します。

    しかしOracleのドキュメントでさえも

まれに、仮想マシンが中断することがあります。つまり、正常にシャットダウンせずに実行を停止します。

これは、仮想マシンが外部で終了した場合(たとえば、UNIXの場合はSIGKILLシグナル、Microsoft Windowsの場合はTerminateProcessコールなど)で発生します。たとえば、内部データ構造を破損したり、存在しないメモリにアクセスしようとしたりすることによって、ネイティブメソッドが失敗した場合にも、仮想マシンが中断することがあります。仮想マシンが中断した場合、シャットダウンフックが実行されるかどうかについて保証はありません。

結論try{} catch{} finally{}ブロックを適切に使用し、finally(}ブロック内の重要なリソースを解放する。finally{}ブロック内のリソースの解放中に、ExceptionおよびThrowableをキャッチする。

4
Ravindra babu

それが単なる記憶であれば、心配しないでください。それはまともな仕事をするだけでGCを信頼する。私は実際にそれが非常に効率的であることを見ました、それはパフォーマンスのために小さいオブジェクトのヒープを作成するためにいくつかの例で大きな配列を利用するより良いかもしれないということを見ました。

4
John Nilsson

finalize()関数はデストラクタです。

ただし、GCの後が呼び出されるため、通常は使用しないでください。また、いつ発生するかはわかりません。

さらに、finalize()を持つオブジェクトの割り当てを解除するには、複数のGCが必要です。

try{...} finally{...}ステートメントを使用して、コード内の論理的な場所を整理してください。

4
Shimi Bandiel

おそらく、オブジェクトを使用している制御フロー内でそのオブジェクトを終了するためにtry ... finallyブロックを使用できます。もちろんそれは自動的には起こりませんが、C++では破壊も起こりません。あなたはしばしばfinallyブロックの中でリソースのクローズを見ます。

3

Javaアプレットを書いている場合は、アプレットのdestroy()メソッドをオーバーライドできます。それは….

 * Called by the browser or applet viewer to inform
 * this applet that it is being reclaimed and that it should destroy
 * any resources that it has allocated. The stop() method
 * will always be called before destroy().

明らかに あなた が欲しいものではありませんが、他の人が探しているものかもしれません。

1
Dave

Javaのデストラクタに最も近いものは finalize() メソッドです。従来のデストラクタとの大きな違いは、いつ呼び出されるのかわからないということです。それがガベージコレクタの責任だからです。ファイルハンドルなどの一般的なRAIAパターンはfinalize()では確実に機能しないため、使用する前にこれを注意深く読むことを強くお勧めします。

1
Nik

Lombokには @Cleanup という注釈があり、ほとんどC++のデストラクタに似ています。

@Cleanup
ResourceClass resource = new ResourceClass();

(コンパイル時に)それを処理するとき、Lombokは適切なtry-finallyブロックを挿入して、実行が変数のスコープから出たときにresource.close()が呼び出されるようにします。リソースを解放するための明示的に別の方法を指定することもできます。 resource.dispose()

@Cleanup("dispose")
ResourceClass resource = new ResourceClass();
1
Alexey

JavaのGCテクノロジにはかなりの進歩がありましたが、それでも参照に注意する必要があります。実際にはネズミがフードの下に巣を作っているように見える一見些細な参照パターンの多数のケースが思い浮かぶ。

あなたの投稿から、オブジェクトの再利用を目的としてresetメソッドを実装しようとしているようには思えません(本当ですか?)。クリーンアップする必要がある他の種類のリソース(つまり、閉じる必要があるストリーム、返される必要があるプールされたオブジェクト、または借用されたオブジェクト)を保持していますか。あなたが心配している唯一のものがメモリ割り当て解除であるならば、私は私のオブジェクト構造を再考し、私のオブジェクトがGC時にクリーンアップされる自己完結型構造であることを検証しようとします。

0
tyler

Javaにはデストラクタクラス、Javaではガベージコレクタによって自動的に破棄されるクラスはありません。しかし、あなたはそれを下の1つを使ってすることができました、しかし、それはまったく同じことではありません:

finalize()

ファイナライズの詳細な議論を生み出した質問 があったので、必要に応じてもっと深く理解してください。

0
Manas

ここには多くのすばらしい答えがありますが、 finalize() を使用しない理由についての追加情報があります。

System.exit()またはRuntime.getRuntime().exit()が原因でJVMが終了した場合、ファイナライザーはデフォルトでは実行されません。 Runtime.exit()のJavadoc

仮想マシンのシャットダウンシーケンスは2つのフェーズで構成されます。最初のフェーズでは、登録されているすべてのシャットダウンフック(ある場合)は、指定されていない順序で開始され、終了するまで同時に実行できます。 2番目のフェーズでは、終了時のファイナライゼーションが有効になっている場合、呼び出されていないすべてのファイナライザが実行されます。これが完了すると、仮想マシンは停止します。

System.runFinalization() を呼び出すことができますが、それは「未解決のすべてのファイナライズを完了するための最善の努力」のみを保証します。

System.runFinalizersOnExit() メソッドがありますが、使用しないでください-安全ではないため、非推奨です。

0
kaan

元の質問について考えてみてください。これは、他のすべての学習された回答から、およびBlochの本質的な 効果的なJava の7から、「ファイナライザを避ける」という解決策を探すことでも成り立ちます。 Java言語には不適切な方法での正当な質問

... OPが実際に望んでいることをするためには、リセットする必要があるすべてのオブジェクトをある種の「プレーンタイプ」に保持し、リセットできない他のすべてのオブジェクトは何らかの方法でしか参照できないようにします。アクセサオブジェクトの...

そして、「リセット」する必要があるときには、既存のベビーサークルを外して新しいベビーサークルを作成します。つまり、ベビーサークル内のすべてのオブジェクトのWebがキャストされ、戻ってくることはありません。

これらのオブジェクトのうちのどれかがCloseableである(あるいはそうではないがcloseメソッドを持っている)場合は、それらを作成時に(そして場合によってはオープン時も)PlaypenのBagに入れることができます。それらを閉じているすべてのCloseablesを通過するのですか…?

コードはおそらく次のようになります。

accessor.getPlaypen().closeCloseables();
accessor.setPlaypen( new Playpen() );

closeCloseablesはおそらくブロッキングメソッドで、おそらくCountdownLatchに特定のスレッド、特にJavaFXスレッドで終了するためにRunnables/Callablesを処理する(そして適切に待機する)ためのラッチ(例えばPlaypen)を含むでしょう。 。

0
mike rodent