へっぽこITエンジニア@名古屋のブログ

Follow me on GitHub

Javaでcloseを書かないとどうなるか

Javaでファイル参照などすると
finallyでクローズ処理を書くことがあると思います。

  } finally {
    xxx.close();
  }

Java7からtry-with-resourcesが使えるので
tryの後にカッコで書けばAutoCloseableが実装
されていれば勝手にcloseされるようになってます。
しかし、もし、close処理を怠ったらどうなるのでしょうか。

目次

ファイルディスクリプタの上限

ファイルディスクリプタ Linuxではファイルディスクリプタというのがあって、
OSがどのファイル・コネクションを操作するかなどを判断しています。

そして、このファイルディスクリプタはファイルのオープンや
コネクションを張ったりすると増えていきます。
悪いことに、同一ファイルだとしても2回オープンすると2つ作られます。

プロセスIDがわかれば、以下のフォルダを見れば状況を確認できます。

/proc/{プロセスID}/fd

そして、このファイルディスクリプタには
上限があり、デフォルトは1024です。
上限は増やすこともできますが、上限を超えると、
問題が発生します。
一時的にプロセスが立って消えるものでは、
あまり問題ないかもしれませんが、
APサーバなどずっと動いてるものだと、いきなり爆発します。
その事例を紹介します。

APサーバが勝手にシャットダウン

APサーバの仕様によると思いますが、使っていたAPサーバは
ファイルディスクリプタの上限に達するとしれっとシャットダウンしました!
ログに、「上限に達したので落とします」的な内容が書かれていましたw
どうしようもないのでシャットダウンすのは仕方ありませんが、パニックですねw

ログを大量に出力してディスクフル

「ファイルアクセス数の上限に達しました」的なログを1秒間に何十、何百行も出力して
ディスクフルになりました。鳴りやまない警告( ;∀;)
ディスクフルになるとそのフォルダを
共有しているほかのプロセスも動かなくなって
色々悲しいことが発生します(*‘▽’)

APサーバのバグを誘発してハング

これが一番最悪でした。
APサーバに対してコネクションを張っている常駐プロセスがいて、
そのプロセスがAPサーバに対して数分に1回コネクションを張っていました。
もちろんクローズせずに・・・
APサーバの方はファイルディスクリプタの上限を増やしているので、
先に常駐プロセスのほうが落ちてしまいます。
落ちるときに、強制的にコネクションがクローズされるのですが、
その際に1000近くのコネクションが一気にクローズされます。

1000件のクローズ

そのAPサーバは古いため致命的なバグがありました。クローズ処理が並列でタイミング悪く重なると、
グローバル変数で大事な変数が書き換わって固まるのです・・・
ですので、1000ものクローズ処理が一気に走るので、週1くらいのペースで固まっていました・・・

しかも悪いことに、その原因を探るためにデバッグログを設定すると、
処理がある程度直列になるため、ほとんど発生しなくなりました・・・

僕はその当時、そのバグを調査しろと言われて毎週上司から詰められてましたw
どうやったらバグを見つけられるか戦略を立てろとか、なんで解決できないんだとか、
今から考えても、かなりきつかったですねw

運よくデバッグログを取得できてサポートに問い合わせた解決できたのですが、
その時のサポートの解答も最悪でした。
実は次のバージョンでは直っていると・・・
だったらバグの修正履歴とかで分かんなかったのか?と言いたかったけど、
解決したので、まーいっかなとw

今から考えると常駐プロセスのクローズ処理にさっさと気づけそうですね。
そうすれば、APサーバは直らなくても、ほぼ発生しなくできたのですが、
駆け出しの自分には荷が重かったですね。
へっぽこなのでw
常駐プロセスが落ちるときにハングが発生しているのはわかっていたので、
もっと常駐プロセスのプログラムを調査すべきでしたね。

まとめ

close処理は怠ると、様々な不具合につながります。
短期のテストでは発生しないので見落としがちです。
ずっと動かすサーバとかなるといつか発生します。
try-with-resourcesなどを利用して、書く量も減るので、
忘れずに書いてくださいね。
しばらく動かしてファイルディスクリプタの数が増えていっていないか
定期的に確認するのもよいかもしれません。
/proc/{プロセスID}/fdで見えるので試してみてください。

作成日:2021-05-20  更新日:2021-05-31