finalizeメソッドがJavaに含まれているのはなぜですか?


44

この投稿によると、呼び出されるfinalizeメソッドに依存すべきではありません。では、なぜJavaがプログラミング言語にそれを含めたのでしょうか?

呼び出される可能性のある関数をプログラミング言語に含めるのは恐ろしい決断のようです。

回答:


41

Joshua BlochのEffective Java(第2版)によると、finalize()役に立つシナリオは2つあります。

  1. 1つは、オブジェクトの所有者が明示的な終了メソッドの呼び出しを忘れた場合の「セーフティネット」として機能することです。ファイナライザがすぐに呼び出されるという保証はありませんが、クライアントが明示的な終了メソッドの呼び出しに失敗した場合(まれに)に、リソースを決して解放しないほうがよい場合があります。ただし、ファイナライザは、リソースが終了していないことを検出した場合、警告を記録する必要があります

  2. ファイナライザの2番目の合法的な使用法は、ネイティブピアを持つオブジェクトに関するものです。ネイティブピアは、通常のオブジェクトがネイティブメソッドを介して委任するネイティブオブジェクトです。ネイティブピアは通常のオブジェクトではないため、ガベージコレクターはそのことを認識せず、Javaピアが回収されるときに回収することはできません。ファイナライザーは、ネイティブピアが重要なリソースを保持していないことを前提として、このタスクを実行するための適切な手段です。ネイティブピアが、すぐに終了する必要のあるリソースを保持している場合、上記のように、クラスには明示的な終了メソッドが必要です。終了メソッドは、重要なリソースを解放するために必要なことをすべて実行する必要があります。

詳細については、27ページの項目7を参照してください。


4
#2の場合、ネイティブピアはセーフティネットを必要とするネイティブリソースであり、終了方法が必要であるため、別のポイントにすべきではありません。
Mooingダック14年

2
@MooingDuck:#2は別のポイントです。ネイティブピアが重要なリソースを保持していない場合(純粋にメモリ内オブジェクトなど)、明示的な終了メソッドを公開する必要がないためです。#1のセーフティネットは、終了方法を明示することです。
ジョミナル14年

55

ファイナライザは、ネイティブリソースの管理に重要です。たとえば、オブジェクトは、非Java APIを使用してオペレーティングシステムからWidgetHandleを割り当てる必要がある場合があります。オブジェクトがGCされたときにそのWidgetHandleをリリースしないと、WidgetHandlesがリークすることになります。

重要なのは、「ファイナライザーが呼び出されない」ケースが、単に単純に故障することです。

  1. プログラムはすぐにシャットダウンします
  2. オブジェクトは、プログラムの存続期間中「永久に」存続します
  3. コンピューターの電源が切れる/ OSによってプロセスが強制終了される/など

これらの3つのケースすべてで、ネイティブリークがないか(プログラムが実行されていないため)、または非ネイティブリークが既にあります(管理オブジェクトを割り当てずに管理オブジェクトを割り当て続ける場合) GC'd)。

「呼び出されるファイナライザに依存しない」という警告は、実際にはプログラムロジックにファイナライザを使用しないことに関するものです。たとえば、構築中にファイルのカウンターをインクリメントし、ファイナライザーでデクリメントすることで、プログラムのすべてのインスタンスに存在するオブジェクトの数を追跡したくない場合があります-オブジェクトが保証されるわけではないためですファイナライズされると、このファイルカウンタはおそらく0には戻らないでしょう。これは、プログラムが正常に終了する(電源障害など)ことに依存すべきではない、より一般的な原則の特別なケースです。

ただし、ネイティブリソースの管理では、ファイナライザが実行されない場合は、実行されなくても構わない場合に対応します。


素晴らしい答え。呼び出されるかもしれませんが、まだ含まれているべきです。StackOverflowの質問とリンクされた質問でしばらく混乱しました。
情報14年

3
それは問題ではありませんでしたが、「呼び出されるファイナライザに依存しない」という文脈で議論する価値があります。ファイナライザが呼び出される可能性がありますが、オブジェクトが到達不能になってから任意の長い遅延の後だけです。これは、メモリよりはるかに少ない「ネイティブリソース」にとって重要です(ほとんどのリソースであることが判明)。

26
「ファイナライザーは、ネイティブリソースの管理に重要です。」-nooooooooo、すみません。ネイティブリソースを管理するためにファイナライザを使用するのは恐ろしい考えです(だからこそ、現在は自動閉鎖可能で共同作業を行っています)。GCはメモリのみを考慮し、他のリソースは考慮しません。これは、ネイティブリソースが不足しても、GCをトリガーしない十分なメモリがあることを意味します。これは本当に望ましくありません。また、ファイナライザはGCをより高価にしますが、これもお勧めできません。
VOO

12
ファイナライザー以外の明示的なネイティブリソース管理戦略を引き続き使用する必要があることに同意しますが、オブジェクトそれらを割り当て、ファイナライザーでそれらを解放しない場合、オブジェクトが存在する場合にネイティブリークが発生しますリソースを明示的に解放せずにGCしました。つまり、ファイナライザはネイティブのリソース管理戦略の重要な部分であり、一般的な問題の解決策ではありません。
ライアンキャバノー14年

1
@Vooおそらく、オブジェクトのコントラクトに従わない場合(close()到達不能になる前に呼び出されない場合)にファイナライザーがフォールバックであると言う方が正しいでしょう
ラチェットフリーク14年

5

このメソッドの目的は、APIドキュメントで次のように説明されています。

Java仮想マシンが、他のオブジェクトのファイナライズによって実行されたアクションの結果を除いて、まだ終了していないスレッドがこのオブジェクトにアクセスできる手段がなくなったと判断した場合に呼び出されます。または完成する準備ができているクラス...

finalize... の通常の目的は、オブジェクトが取り消せなく破棄される前にクリーンアップアクション実行することです。たとえば、入出力接続を表すオブジェクトのfinalizeメソッドは、オブジェクトが永久に破棄される前に明示的なI / Oトランザクションを実行して接続を切断する場合があります...


言語デザイナーが、アプリケーションプログラマーの制御を超えた方法(「絶対に頼るべきではない」)で「オブジェクトを取り消せないように破棄する」(ガベージコレクション)を選択した理由にさらに興味がある場合は、関連する回答で説明されています質問

自動ガベージコレクション...は、CおよびC ++プログラマを悩ますプログラミングエラーのクラス全体を排除します。システムが多くのエラーを迅速に検出し、本番コードが出荷されるまで主要な問題が休止状態にならないことを確信して、Javaコードを開発できます。

上記の引用は、Java設計目標に関する公式文書から引用されたものです。つまり、Java言語設計者がこのように決定した理由を説明する信頼できる参照と見なすことができます。

この設定のより詳細で言語にとらわれない議論については、OOSCセクション9.6自動メモリ管理を参照してください(実際、このようなセクションだけでなく、そのようなものに興味があるなら、第9章全体を読む価値があります)。このセクションは、明確なステートメントで開きます。

優れたオブジェクト指向環境は、到達不能なオブジェクトを検出して再生する自動メモリ管理メカニズムを提供する必要があり、アプリケーション開発者は自分の仕事であるアプリケーション開発に専念できます。

このような機能を使用可能にすることがいかに重要かを示すには、前述の説明で十分です。Michael SchweitzerとLambert Stretherの言葉:

自動メモリ管理のないオブジェクト指向プログラムは、安全弁のない圧力鍋とほぼ同じです。遅かれ早かれ、確実に爆発するでしょう!


4

ファイナライザが存在するのは、(実際にはそうではないにせよ)物事がクリーンアップされることを保証する効果的な手段であることが期待されたため、また、発明されたときにクリーンアップを確保するためのより良い手段(ファントム参照や試用など) -resources)はまだ存在していませんでした。後から考えると、Javaは「finalize」機能の実装に費やされた努力が他のクリーンアップ手段に費やされた場合に優れていると思われますが、Javaが最初に開発されていた間はほとんど明確ではありませんでした。


3

警告:私は時代遅れかもしれませんが、これは数年前の私の理解です:

一般に、ファイナライザの実行の保証はありません-または、まったく実行されるという保証さえありませんが、一部のJVMでは、プログラムが終了する前に完全なGCおよびファイナライズを要求できます(もちろん、プログラムに時間がかかります終了します。これはデフォルトの動作モードではありません)。

また、一部のGCは、これによりベンチマークでのパフォーマンスが向上することを期待して、ファイナライザを持つGCオブジェクトを明示的に遅延または回避することが知られていました。

残念ながら、これらの動作は、ファイナライザが推奨された元の理由と矛盾するため、代わりに明示的に呼び出されるシャットダウンメソッドの使用を推奨しています。

破棄する前に実際にクリーンアップする必要があるオブジェクトがあり、ユーザーが本当にそうすることを信頼できない場合は、ファイナライザーを検討する価値があります。しかし一般に、初期の例のいくつかで見たように、現代のJavaコードではそれほど頻繁に見ないという理由があります。


1

GoogleのJavaのスタイルガイドは、件名にいくつかの賢明なアドバイスがあります:

オーバーライドすることは非常にまれObject.finalizeです。

ヒント:しないでください。どうしても必要な場合は、最初に「有効なJava項目7」「ファイナライザーを回避する」をよく読んで理解し、それを実行しないでください。


5
私が問題の単純化を賞賛する理由、「それをしない」は「なぜ存在するのか」に対する答えではありません。
ピエールアラード14年

1
私は自分の答えでそれをうまく綴らなかったと思いますが、(IMO)「なぜそれが存在するのか?」に対する答えです。「するべきではない」です。ファイナライズ操作が本当に必要なまれなケースでは、おそらく自分自身でビルドしPhantomReferenceReferenceQueue代わりにビルドしたいでしょう。
ダニエル・プライデン14年

私はあなたを支持しませんでしたが、明らかに*を意味しました。しかし、最も支持された答えは、この方法がいかに有用であるかを示しており、ちょっとポイントを無効にします。
ピエールアラード

1
ネイティブのピアの場合でもPhantomReference、より良いソリューションだと思います。ファイナライザは、Javaの初期の時代から残されたいぼであり、同様のObject.clone()タイプや生のタイプは、最も忘れられている言語の一部です。
ダニエル・プライデン14年

1
ファイナライザーの危険性は、C#でよりアクセスしやすい(開発者が理解できる)方法で説明されています。、しかし、JavaとC#の基礎となるメカニズムが同じであることを暗示してはなりません。彼らの仕様にはそういうことは何もありません。
rwong 14

1

Java言語仕様(Java SEの7)は述べています:

ファイナライザは、自動ストレージマネージャによって自動的に解放できないリソースを解放する機会を提供します。そのような状況では、オブジェクトが使用するメモリを単に再生するだけでは、そのオブジェクトが保持しているリソースが再生されることを保証しません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.