Effective Javaを読むチャレンジ-項目7-

項目7 ファイナライザを避ける

この本読むときには必ず読み飛ばしていた項目ですが、今回は真面目に向き合います。

ファイナライザって・・・

ファイナライザとはjava.lang.Objectのfinalizeメソッドをオーバーライドしたオブジェクトのことです。

即座にファイナライザが実行される保証はない

なぜ保証がないのかというと、JVMによってファイナライザのオブジェクトにメモリが割り当てられたと同時に、ファイナライザは管理用のリストに溜まります

で、そのオブジェクトの参照がファイナライザの管理リストだけになったときに、finalizeメソッドが実行されます

ファイナライザはfinalizeメソッドが実行され、ファイナライザの管理リストからの参照がなくなったときに初めてGC対象になり、その次のGCの際にメモリが回収されます。finalizeメソッドを確実に実行するため、オブジェクトの参照をヒープに留めておかなければならないからです。即座にファイナライザが実行される保証はないと筆者が言っているのは、この事実が前提だと思います。本題はこの先。

ファイナライザを実行するスレッドは、アプリケーションが実行されるスレッドとは違います。そして、そのファイナライザスレッドがファイナライザを実行する順番は分からないということです。いつfinalizeメソッドが実行されるかアプリケーションからは分からない、しかも実行されたあといつGCが動くかも分からないということになれば、即時性の保証はないということになりますね。

即時性が保証されない(即時実行されるかもしれないし、GCされないままアプリケーションが終了することもあるそうです)ということは、回収が間に合わないオブジェクトがファイナライザの管理リストに溜まり続け、OutOfMemoryErrorを起こすことに繋がるとも言っています。

この当たりのことは、Java言語仕様に書いてあります。

Java言語仕様には、さらにfinalize実行中にキャッチされない例外が発生すると、例外は無視され、finalizeは終了するとあります。

あと本書ではファイナライザのオブジェクト生成はファイナライザではない場合に比べてかなり遅いとあります。実際、パフォーマンスを理由にfinalizeメソッドの処理を書いていないjava本体のクラスもあります。

リソースの解放はfinalizeでやるな

ファイルのオープンやDBコネクションなどのリソースの解放はtry-finallyでやれ、ということです。Java SE 7以降ならtry-with-resource構文が便利ですね。これはコーディング規約として一般的なのでまあいいです。

ファイナライザが有効な場合1:クライアントのclose忘れのための安全ネット

例えばjava.io.FileInputStreamクラスはそのためにfinalizeメソッドをオーバーライドしていますね。

ファイナライザが有効な場合2:ネイティブピアを持つオブジェクト

例えば、java.awtパッケージでそういうことをしていそうですが・・・

ファイナライザ連鎖は自動的に実行されない

ファイナライザのサブクラスは明示的にスーパークラスのfinalizeを呼び出さないと、スーパークラスのfinalizeは実行されないそうです。

なので、サブクラスがfinalizeを呼び忘れないように、外側のクラスのファイナライザを実行するためだけの無名クラスを使い、Objectメンバを用意するといいのだそう。それをファイナライザガーディアンと筆者は呼んでいます。

が、javaパッケージにはそういった実装が見当たらない・・・ひとまず終了。

広告
  • LINEで送る