11

javaやc#などの言語にファイナライザーがある理由がよくわかりません。AFAIK、彼ら:

  • 実行が保証されていません(Javaで)
  • それらが実行される場合、問題のオブジェクトがファイナライズの候補になった後、任意の時間実行される可能性があります
  • そして(少なくともJavaでは)、クラスに固執することすら驚くほど大きなパフォーマンスヒットを被ります。

では、なぜそれらが追加されたのでしょうか。友人に聞いたところ、「DB接続などをクリーンアップする機会があればいいのに」とつぶやいたのですが、これは悪い習慣だと思います。最後の防衛線としてさえ、なぜあなたは何かのために上記の特性を持つ何かに頼る必要がありますか?特に、似たようなものがAPIに設計されている場合、そのAPIは存在しなくなって笑われるでしょう。

4

9 に答える 9

16

まあ、それらは特定の状況で、信じられないほど便利です。

.NET CLRでは、たとえば次のようになります。

  • 実行が保証されていません

プログラムが強制終了されない場合、ファイナライザーは常に最終的に実行されます。いつ実行されるかは決定論的ではありません。

  • それらが実行される場合、問題のオブジェクトがファイナライズの候補になった後、任意の時間実行される可能性があります

これは真実ですが、それでも実行されます。

.NETでは、これは非常に便利です。.NETでは、ネイティブの非.NETリソースを.NETクラスにラップするのが非常に一般的です。ファイナライザーを実装することにより、ネイティブリソースが正しくクリーンアップされることを保証できます。これがないと、ユーザーはクリーンアップを実行するメソッドを呼び出す必要があり、ガベージコレクターの効率が大幅に低下します。

(ネイティブ)リソースをいつリリースするかを正確に知ることは必ずしも簡単ではありません。ファイナライザーを実装することで、クラスが完全ではない方法で使用されている場合でも、リソースが正しくクリーンアップされることを保証できます。

  • そして(少なくともJavaでは)、クラスに固執することすら驚くほど大きなパフォーマンスヒットを被ります

ここでも、.NETCLRのGCには利点があります。適切なインターフェース(IDisposable)を実装し、開発者がそれを正しく実装すれば、ファイナライズの費用のかかる部分が発生するのを防ぐことができます。これを行う方法は、クリーンアップを実行するためのユーザー定義メソッドが、ファイナライザーをバイパスする GC.SuppressFinalizeを呼び出すことができるというものです。

これにより、両方の長所が得られます。ファイナライザーとIDisposableを実装できます。ユーザーがオブジェクトを正しく破棄した場合、ファイナライザーは影響を与えません。そうでない場合、ファイナライザーは(最終的に)実行され、管理されていないリソースをクリーンアップしますが、実行中に(わずかな)パフォーマンスの低下が発生します。

于 2009-12-05T01:29:26.613 に答える
10

うーん、ここに描かれている絵は少しバラ色が強すぎます。ファイナライザーは、.NET での実行も保証されていません。典型的な事故は、ファイナライザー スレッドで例外またはタイムアウト (2 秒) をスローするファイナライザーです。

これは、Microsoft が SQL Server で .NET ホスティング サポートを提供することを決定したときの問題でした。リソース リークを解決するためにアプリを再起動するようなアプリケーションは、実行可能な回避策とは見なされません。.NET 2.0 では、CriticalFinalizerObject クラスから派生することで有効になる重要なファイナライザーが取得されました。このようなクラスのファイナライザーは、制約付き実行領域 (CER) の規則に従う必要があります。これは、本質的に例外が抑制されるコードの領域です。CER でできることは非常に限られています。

元の質問に戻りますが、メモリ以外のオペレーティング システム リソースを解放するには、ファイナライザーが必要です。ガベージ コレクターはメモリを適切に管理しますが、ペン、ブラシ、ファイル、ソケット、ウィンドウ、パイプなどを解放するために何もしません。それ。ファイナライザーは、プログラムがそうするのを忘れた場合でも、それが確実に行われるようにします。自分でファイナライザーを使用してクラスを作成することはほとんどありません。操作リソースはフレームワーク内のクラスによってラップされます。

.NET フレームワークには、そのようなリソースが確実に早期にリリースされるようにするためのプログラミング パターンもあるため、ファイナライザーが実行されるまでリソースが残りません。ファイナライザーを持つすべてのクラスは IDisposable.Dispose() メソッドも実装するため、コードでリソースを明示的に解放できます。これは .NET プログラマーによって忘れられることがよくありますが、ファイナライザーによって最終的に行われることが保証されるため、通常は問題が発生することはありません。多くの .NET プログラマーは、すべての Dispose() 呼び出しが処理されているかどうかを心配して何時間も眠れず、フォーラムでそれに関する膨大な数のスレッドが開始されています。Java 関係者はもっと幸せになれるはずです。


コメントのフォローアップ: ファイナライザー スレッドの例外とタイムアウトは、心配する必要はありません。まず、ファイナライザーを作成していることに気付いたら、深呼吸して、自分が正しい道を進んでいるかどうかを自問してください。ファイナライザーはフレームワーク クラス用です。そのようなクラスを使用して操作リソースを使用する必要があります。そのクラスにファイナライザーを無料で組み込むことができます。SafeHandle クラスに至るまで、重要なファイナライザーがあります。

第 2 に、ファイナライザー スレッドの障害は、全体的なプログラムの障害です。OutOfMemory 例外を取得したり、電源コードにつまずいてマシンのプラグを抜いたりするのと同様です。コードのバグを修正するか、ケーブルを再配線する以外に、それらについてできることは何もありません。Microsoft が重要なファイナライザーを設計することは重要でした。Microsoft は、SQL Server の .NET コードを作成するすべてのプログラマーがそのコードを正しく作成できるとは限りません。自分でファイナライザーをいじっても、そのような責任はありません。Microsoft ではなく、顧客から電話を受けるのはあなたです。

于 2009-12-05T02:17:25.440 に答える
4

finalize() のJavaDocを読むと、「ガベージコレクションがオブジェクトへの参照がこれ以上ないと判断したときに、オブジェクトのガベージコレクタによって呼び出されます。サブクラスは、システムリソースを破棄するか実行するためにファイナライズメソッドをオーバーライドします。その他のクリーンアップ。」

http://java.sun.com/javase/6/docs/api/java/lang/Object.html#finalize

それが「理由」です。それらの実装が効果的であるかどうかについては、議論できると思います。

私が見つけた finalize() の最適な使用法は、プールされたリソースを解放することでバグを検出することです。リークしたオブジェクトのほとんどは最終的にガベージ コレクションを取得し、デバッグ情報を生成できます。

class MyResource {
    private Throwable allocatorStack;

    public MyResource() {
        allocatorStack = new RuntimeException("trace to allocator");
    }

    @Override
    protected void finalize() throws Throwable {
        try {
            System.out.println("Bug!");
            allocatorStack.printStackTrace();
        } finally {
            super.finalize();
        }
    }
 }
于 2009-12-05T01:42:32.110 に答える
4

Java ファイナライザには、外部リソース (JVM の外部に存在し、「親」Java オブジェクトがある場合にガベージ コレクションできないもの) のクリーンアップを可能にするために存在します。これは常にまれでした。例として、カスタム ハードウェアと接続している場合があります。

Java のファイナライザーの実行が保証されていない理由は、プログラムの終了時にそうする機会がない可能性があるためだと思います。

「純粋な」Java でファイナライザーを使用して行うことの 1 つは、それを使用して終了条件をテストすることです。たとえば、すべての接続が閉じていることを確認し、そうでない場合はエラーを報告します。エラーが常にキャッチされるとは限りませんが、少なくともバグを明らかにするのに十分な時間はキャッチされる可能性があります。

ほとんどの Java コードには、ファイナライザーの呼び出しがありません。

于 2009-12-05T01:44:32.340 に答える
2

これらは、オブジェクトへのすべての参照が壊れるまで解放できないネイティブ リソース (ソケット、開いているファイル、デバイスなど) を解放することを目的としています。知っている。別の方法は、微妙で追跡不可能なリソースリークです...

もちろん、多くの場合、アプリケーションの作成者は、DB 接続への参照が 1 つしかないことを知っているでしょう(たとえば)。その場合、ファイナライザーは、終了したことがわかっているときに適切に閉じる代わりにはなりません。

于 2009-12-05T01:42:29.507 に答える
0

.Netランドでは、実行時にtが保証されません。しかし、彼らは実行されます。

于 2009-12-05T01:31:14.173 に答える
0

Object.Finalizeを参照していますか?

msdnによると、「C#コードでは、Object.Finalizeを呼び出したりオーバーライドしたりすることはできません」。実際、Disposeメソッドを使用することをお勧めします。これは、より制御しやすいためです。

于 2009-12-05T01:33:42.627 に答える