640

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

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

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

データを逆参照してガベージ コレクターがデータを収集するのを待つだけだとしたら、ユーザーが繰り返しデータを入力してリセット ボタンを押すと、メモリ リークが発生しないのではないかと考えていました。また、Java は言語として非常に成熟しているため、これが起こらないようにするか、適切に対処する方法があるはずだと考えていました。

4

25 に答える 25

569

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

という継承されたメソッドがありますが、finalizeこれは完全にガベージ コレクターの裁量で呼び出されます。そのため、明示的に整理する必要があるクラスでは、closeメソッドを定義し、finalize をサニティ チェックにのみ使用するのが慣例です (つまり、closeが呼び出されていない場合は、すぐに実行してエラーをログに記録します)。

最近、ファイナライズに関する詳細な議論を引き起こした質問があったため、必要に応じてより詳細な情報を提供する必要があります...

于 2008-10-05T13:17:15.533 に答える
141

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また、アプリケーションの実行中にメソッドが呼び出されるという保証はまったくありません。

于 2011-12-02T03:25:14.493 に答える
115

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

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

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

于 2008-10-05T13:20:20.660 に答える
31

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

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

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

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


編集:

データを逆参照してガベージ コレクターがデータを収集するのを待つだけなら、ユーザーが繰り返しデータを入力してリセット ボタンを押した場合、メモリ リークが発生しないのではないかと考えていました。

通常、必要なのはオブジェクトを参照解除することだけです。少なくとも、これが動作するはずの方法です。ガベージ コレクションが心配な場合は、Java SE 6 HotSpot[tm] Virtual Machine Garbage Collection Tuning (またはお使いの JVM バージョンの同等のドキュメント) を確認してください。

于 2008-10-05T13:50:53.500 に答える
24

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()たとき、およびブロックが実行される前に が実行されます。メソッドの場合とは異なり、実行が保証されます。ただし、節で明示的に実行する必要はありません。trycatchfinallyfinalize()close()finally

于 2012-07-30T15:30:09.143 に答える
15

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

try-catch-finally ブロックに加えて、Runtime#addShutdownHook (Java 1.3 で導入) を使用して、プログラムの最終的なクリーンアップを実行できます。

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

これの利点は、プログラムの残りの部分から疎結合された分解動作を持つことです。

于 2008-10-05T14:03:21.667 に答える
12

いいえ、java.lang.Object#finalizeあなたが得ることができる最も近いです。

ただし、いつ (そしてもし) 呼び出されるかは保証されません。
見る:java.lang.Runtime#runFinalizersOnExit(boolean)

于 2008-10-05T13:14:11.967 に答える
7

まず、Java はガベージ コレクションであるため、オブジェクトの破棄について何かを行う必要があることはめったにないことに注意してください。第一に、通常、解放する管理対象リソースがないため、第二に、いつ、またはいつ発生するかを予測できないため、「誰もオブジェクトを使用しなくなったらすぐに」発生する必要があることには不適切であるためです。 "。

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

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

デストラクタのように見えますが、デストラクタのようには動作しない finalize() もあります。通常、これは適切なオプションではありません。

于 2008-10-05T15:42:26.927 に答える
6

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

ただし、 GC の後に呼び出され、いつ発生するかがわからないため、通常は使用しないでください (発生した場合)。

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

try{...} finally{...}ステートメントを使用して、コード内の論理的な場所をクリーンアップするようにしてください。

于 2008-10-05T13:16:46.940 に答える
6

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

finalizeまたはのいずれかに完全に依存するべきではありません。ShutdownHook

ファイナライズ

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

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

  3. アプリケーションには、ガベージ コレクションが呼び出されないライブ オブジェクトがいくつかある場合があります。

  4. Exceptionfinalizing メソッドによってスローされたものはすべて、GC スレッドによって無視されます

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

シャットダウンフック

public void addShutdownHook(Thread hook)

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

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

  1. 最後の非デーモン スレッドが終了するか、exit (同等のSystem.exit) メソッドが呼び出されると、プログラムは正常に終了します。

  2. 仮想マシンは、^C の入力などのユーザー割り込み、またはユーザー ログオフやシステム シャットダウンなどのシステム全体のイベントに応答して終了します。

  3. シャットダウン フックは、単に初期化されているが開始されていないスレッドです。仮想マシンがシャットダウン シーケンスを開始すると、登録されているすべてのシャットダウン フックが不特定の順序で開始され、同時に実行されます。finalization-on-exit が有効になっている場合、すべてのフックが終了すると、呼び出されていないすべてのファイナライザーが実行されます。

  4. 最後に、仮想マシンが停止します。exit メソッドの呼び出しによってシャットダウンが開始された場合、非デーモン スレッドと同様に、デーモン スレッドはシャットダウン シーケンス中も実行し続けることに注意してください。

  5. シャットダウン フックもすばやく作業を終了する必要があります。プログラムが exit を呼び出すと、仮想マシンがすぐにシャットダウンして終了することが期待されます。

    しかし、Oracleのドキュメントでさえそれを引用しています

まれに、仮想マシンが異常終了する場合があります。つまり、正常にシャットダウンせずに実行を停止する場合があります。

これは、仮想マシンが外部で終了した場合に発生します。たとえば、SIGKILLUnix のシグナルまたはTerminateProcessMicrosoft Windows の呼び出しで発生します。たとえば、内部データ構造が破損したり、存在しないメモリにアクセスしようとしたりして、ネイティブ メソッドが失敗した場合にも、仮想マシンは中止される可能性があります。仮想マシンが異常終了した場合、シャットダウン フックが実行されるかどうかは保証されません。

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

于 2016-05-13T15:50:59.350 に答える
4

あなたが心配しているのがただの記憶であれば、気にしないでください。まともな仕事をするGCを信頼してください。非常に効率的であるため、場合によっては大きな配列を使用するよりも、小さなオブジェクトのヒープを作成する方がパフォーマンスが向上する可能性があることを実際に見ました。

于 2008-10-05T16:04:31.860 に答える
3

おそらく、try ... finally ブロックを使用して、オブジェクトを使用している制御フローでオブジェクトをファイナライズできます。もちろん自動的には起こりませんが、C++ での破壊もそうではありません。finally ブロックでリソースが閉じられることがよくあります。

于 2008-10-05T13:51:17.167 に答える
2

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

于 2008-10-05T13:20:01.783 に答える
2

元の質問について考えてみると... 他のすべての学習された回答から、また Bloch の本質的な効果的な Javaの項目 7、「ファイナライザーを回避する」から結論付けることができると思います。 Java 言語には不適切です...:

... OPが実際に望んでいることを行うためのかなり明白な解決策は、リセットする必要があるすべてのオブジェクトを一種の「プレイペン」に保持することではありません。アクセサオブジェクトの...

そして、「リセット」する必要がある場合は、既存のベビーサークルを切断して新しいベビーサークルを作成します。ベビーサークル内のすべてのオブジェクトのウェブは漂流し、二度と戻ることはなく、ある日 GC によって収集されます。

これらのオブジェクトのいずれかが存在するCloseable(または存在しないが、closeメソッドがある) 場合Bag、それらが作成される (そしておそらく開かれる) ときに、それらをベビーサークルに配置できます。それらを閉じるすべてを通してCloseables...?

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

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

closeCloseablesおそらく、特にJavaFXスレッドで、必要に応じて終了する特定のスレッドCountdownLatchを処理する(および必要に応じて待機する)ための、おそらくラッチ(たとえば)を含むブロッキングメソッドです。RunnablesCallablesPlaypen

于 2016-11-03T20:21:24.183 に答える
1

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().

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

于 2009-08-25T14:54:00.307 に答える
0

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

あなたの投稿から、オブジェクトの再利用を目的としてリセットメソッドを実装しようとしているようには聞こえません(本当ですか?)。オブジェクトは、クリーンアップする必要のある他のタイプのリソース(つまり、閉じる必要のあるストリーム、返される必要のあるプールされたオブジェクトまたは借用されたオブジェクト)を保持していますか?心配しているのがメモリの割り当て解除だけである場合は、オブジェクト構造を再検討し、オブジェクトが自己完結型の構造であり、GC時にクリーンアップされることを確認しようとします。

于 2008-10-05T17:39:56.317 に答える
0

Android プログラミングに関しては、 onDestroy() メソッドを呼び出してみてください。これは、Activity/Service クラスが強制終了される直前に実行された最後のメソッドです。

于 2021-09-23T08:05:07.937 に答える