ファイナライザ、クリーナは一般には不要なため、使用することを避けるべき、という内容です。
記載内容
クリーナー、ファイナライザ共通
クリーナー、ファイナライザが実行されるまでの時間は予想できない
実行までの時間的な制約がある処理をクリーナー、ファイナライザで行うべきではない。
例えばファイルのクローズをクリーナー、ファイナライザに頼るべきではない。
クリーナー、ファイナライザの実行が遅れると、オープン可能なファイル数の限界に達し、
新しいファイルのオープンでエラーになる可能性がある。
クリーナー、ファイナライザを実装するよりも、
AutoCloseableを実装し、使用側でtry-with-resourcesを使うべきである。
クリーナーの正当な使い方は二つある。
- クローズを呼び忘れた場合の保険
- ネイティブピアの回収
クリーナー、ファイナライザの実行は言語仕様上は保証されていない
DBのロック開放の様な、永続的な状態の更新をクリーナー、ファイナライザで行うべきではない。
パフォーマンスの劣化
クリーナー、ファイナライザを使用した場合、GCに関して深刻なパフォーマンスの劣化が発生する。
著者の環境では
クリーナー、ファイナライザ呼び出しと伴うGC処理は50倍程度、
クリーナを保険として使用している場合でも5倍程度遅くなる。
ファイナライザ
ファイナライザはJava9より非推奨になっており、使用するべきではない。
ファイナライザはセキュリティ問題を抱えており、悪意のあるサブクラスでファイナライザを実装することで、
不正なオブジェクト参照を取得することが可能になる。
これを防ぐには、クラスをfinalにするか、何もしないfinalのファイナライザを書けば良い。
クリーナー
クリーナーはファイナライザよりは良いが、動作は予想不可能で遅く、また、一般的には使用する必要はない。
クリーナーのAPIは少々扱いづらいが、クラスのpublicのAPIに影響しない。
例として、AutoCloseableを実装したクラスに対して、
クローズ漏れに対する保険としてクリーナーを使用するのは、問題はない。
ただし、クリーナーが確実に実行されるとは限らない。
System.exitや、通常のプログラム終了に対するクリーナーの動作は実装に依存し、
実行は保証されない。
考察
かなり以前から、ファイナライザは使うべきではない、というのが一般的だったと思いますが、
非推奨になっており、明確に使うべきではない、という状態になっています。
ただし、代替となるクリーナーも、セキュリティ以外のファイナライザの欠点である
- 実行タイミングが分からない
- 確実に実行されるとは限らない
はそのままですので、やはり確実な後処理には向かないです。
try-with-resourcesを使い、ファイルのオープンのように、使用上限がある重要な資源であれば、
保険でクリーナーを使用する、程度の使い方にとどまるかと思います。
ただし、GCに関するパフォーマンスの問題がありますので、AutoCloseableなクラス全てで
クリーナーを使う、というのは避けるべきかと思います。