読者です 読者をやめる 読者になる 読者になる

Codeへの愛とCuriosity

CodeIQ への出題・解説解題・その周辺について、思いつくままに。

FileReader が必ず close されるのかどうかの続き

FileReader が必ず close されるのかどうかの続き

FileReader が必ず close されるのかどうか ( http://nabetani.hatenablog.com/entry/2014/10/24/003239
)の続き。

結論

結論から書いてみる。

// 「BufferedReader に任せた」版
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
  // do something.
}
// 「FileReader close しすぎ」版
try (
  FileReader fr = new FileReader(path);
  BufferedReader br = new BufferedReader(fr)
) {
  // do something.
}

のどちらも正当。

私の場合

  • 仕事で製品に入れるなら「FileReader close しすぎ」版を好む
  • CodeIQ の解答とかに書くなら「BufferedReader に任せた」版を好む

と思う。

とはいえ、java.nio.file.Files.newBufferedReader が使えるのなら、そのほうが良い。

私について

実は。業務での Java 歴は1年ぐらいしかない。
CodeIQ で出す問題では、必要ならちゃんとコードを読む言語に入れているけど、正直あんまり自信がない。

状況をもう一度整理

「BufferedReader に任せた」版でマズイことが起こる条件は

A0. BufferedReader の new ( コンストラクタの前 ) でエラーが発生する
A1. BufferedReader のコンストラクタ内で例外・エラーが発生する
B. A で発生した例外・エラーがキャッチされ、処理が続く
C. リークしたハンドルが GC で回収されずに貯まる

という条件の組み合わせ( ( A0 or A1 ) and B and C ) が真になる場合に限られる。

順に見ていこう。

A0. BufferedReader の new ( コンストラクタの前 ) でエラーが発生する

これが発生するのはひどくまずい状況に限られる。
これが発生しない前提の仕事も多いと思う。
発生するのはメモリ不足だけなのか、クラスローダーとかが例外を投げることもあるのか、そのあたりはよくわかっていないんだけど、いずれにせよ、プロセスごと死んでもいい場合が多いと思う。

A1. BufferedReader のコンストラクタ内で例外・エラーが発生する

一方。
BufferedReader のコンストラクタ内には

new char[8192];

のようなことが書いてあり、状況によってはメモリ不足エラーは視野に入れておくべきだと思う。
※ 例外( Exception ) は発生しない。

B. A で発生した例外・エラーがキャッチされ、処理が続く

CodeIQ の問題を解いているとかいう状況なら、エラーはキャッチされずにそのままプロセス終了になる。
問題ない。

一方。
ウェブサービスを動かしているとかいう状況だと、フレームワークにキャッチされる場合も多いと思う。
キャッチされたら即プロセス終了ということならそれで問題ないんだけど、スレッド終了とかいうことになると close されない FileReader が残ることになる。

C. リークしたハンドルが GC で回収されずに貯まる

GC で FileReader が回収されると、FileReader の中にある FileInputStream の finalize で、必要な close が呼ばれ、ファイルハンドルも回収される。と思う。(合ってる?)

「BufferedReader に任せた」版の実装は、FileReader への参照がなくなっていることがすごくわかりやすいので早い段階で GC されると想像する。

結局どうなの?

これが原因で具体的にマズイことになることは極めて稀だと思う。
実際に発生する環境が用意できる気がしない。
稀とはいえ、私の理解が正しければ、「BufferedReader に任せた」版 だとトラブルになり、「FileReader close しすぎ」版 だとトラブルにならないという状況がありうる。

一方。「FileReader close しすぎ」版 は、正常系では FileReader を close しすぎていて、実行速度面でもメモリフットプリントでももったいない感じではある。

というわけで、どちらもメリット・デメリットがあるのでケースバイケースで使い分けるというのが正解だと思う。

で。
具体的には。
ウェブサーバー内で動くとか、組み込み機器内で動くとか言うことなら、「FileReader close しすぎ」版 がよい場合が多いと思う。
デスクトップアプリケーションなら、「FileReader close しすぎ」版 が良い場合が多いと思う。

ケースバイケースなんかめんどくさいからどっちかに決めたい、ということなら、「FileReader close しすぎ」版 が良いと思う。
「常にちょっと close しすぎ」と、「極稀にファイルハンドルリークするかも。しないかも。」だと、前者のほうがマシだと感じる。

とはいえ結局。
nio を使え、ということだと思う。

最後に

識者のツッコミお待ちしております。

追記

どうも、上記話は正しくなさそうな感じ。Java 難しい。