はじめに
最近自宅で以下のSample.java
のようなコードを書いていた。
HttpURLConnection urlconn = null;
PurchaseItemInfo itemInfo = null;
try {
URL url = new URL([URL]);
urlconn = (HttpURLConnection) url.openConnection();
urlconn.setRequestMethod("GET");
urlconn.setInstanceFollowRedirects(false);
urlconn.connect();
try (InputStreamReader inReader = new InputStreamReader(urlconn.getInputStream());
BufferedReader reader = new BufferedReader(inReader);) {
doSomething();
}
} finally {
urlconn.disconnect();
}
これを書くにあたり、HttpURLConnection について調べていたのだが、最後にdisconnect()を呼び出しているサンプルと呼び出していないサンプルの両方があった。
通常、何かしらのリソース(FileInputStream とか Connection とか)を open() したら try-with-resources で AutoClose させるか、finally 句の中で明示的に close() させるかしないと GC の対象になるまでリソースを占有するので必ず close() するのが鉄則だと思っていた。
HttpURLConnection の場合も同じだと思っていたのだが、AutoCloseable インタフェースを implements してないので try-with-resources 使えない(※ご指摘をいただいたのでこの記事の一番最後に指摘を反映したものをあげておきます)し、もしかしてちょっと違うのか?と思い始めたので調べてみた。
HttpURLConnectionのdisconnect()とリソースのclose()の違い
HttpURLConnection(API仕様)には以下のように記載してあった。
単一の要求を行う際には個々のHttpURLConnectionインスタンスが使用されますが、その背後のHTTPサーバーへのネットワーク接続は、ほかのインスタンスと透過的に共有される可能性があります。要求後、HttpURLConnectionのInputStreamまたはOutputStream上でclose()メソッドを呼び出すと、そのインスタンスに関連付けられていたネットワーク・リソースが解放される可能性がありますが、共有されている持続接続への影響はまったくありません。disconnect()メソッドを呼び出した場合、持続接続がその時点でアイドル状態になっていれば、使用していたソケットがクローズされる可能性があります。
この他にも参考のところに書いたサイト等も見てみたところ、HttpURLConnectionをdisconnect()した場合、ソケットがKeepAlive設定でアイドル状態であったとしてもクローズされる可能性がある。
(HttpURLConnectionはデフォルトkeepalive)
HttpURLConnectionをdisconnect()するのではなく、InputStream/OutputStreamをclose()してあげれば、ネットワークリソースは解放されるが、ソケットはクローズされず、再利用される。
ということらしい。
今回みたいに個人でちょっとしたものを作っている場合はあまり意識しなくてもよさそうだが、多くの人が接続するなどの理由で何度も接続/切断を繰り返すようなシステムの場合はちゃんと意識してコーディングしないと、最悪性能問題に発展しかねないですね・・・。
一つ勉強になりました。
参考
・HttpURLConnection#disconnectとKeep-Aliveと
・HttpURLConnection(API仕様)
・HttpURLConnectionはデフォルトkeepalive
・Do we need to call HttpURLConnection.disconnect()?
2019/11/11 ご指摘をいただいたので修正
この記事の「はじめに」の「HttpURLConnection の場合も同じだと思っていたのだが、 AutoCloseable インタフェースを implements してないので try-with-resources 使えない」
という部分についてご指摘をいただきました。
AutoCloseable は Functional インタフェースなのでラムダ式で実装することもできます。
HttpURLConnection urlconn = (HttpURLConnection) url.openConnection();
try (AutoCloseable c = () -> urlconn.disconnect()) {
doSomething();
}
確かに AutoCloseable は close() メソッドしか持たないのでラムダ式使えますね。。。
というわけで以下のよう書くこともできます。
URL url = new URL([URL]);
HttpURLConnection urlconn = (HttpURLConnection) url.openConnection();
urlconn.setRequestMethod("GET");
urlconn.setInstanceFollowRedirects(false);
PurchaseItemInfo itemInfo = null;
try (AutoCloseable c = () -> urlconn.disconnect();
InputStreamReader inReader = new InputStreamReader(urlconn.getInputStream());
BufferedReader reader = new BufferedReader(inReader);) {
urlconn.connect();
doSomething();
}
ご指摘ありがとうございました。
勉強になりました。
以上。