Java.net.http.HttpClient
を閉じて、保持しているリソースを即座に解放する方法はありますか?
内部的には、セレクター、接続プール、およびExecutor
(デフォルトのものが使用されている場合)を保持します。ただし、Closeable
/AutoCloseable
は実装されていません。
お気づきのとおり、Java.net.http.HttpClient
はCloseable
またはAutoCloseable
を実装していません。したがって、私は2つのオプションしか考えられませんが、どちらも本当に防弾ではなく、さらには優れていません。
プログラムが保持しているHttpClient
へのすべての 強力な参照 および ガベージコレクションの要求 を削除できます。ただし、直接制御できない何かがそれまたはそのコンポーネントの1つを保持しているという本当のリスクがあります。強い参照が残っていると、参照されているオブジェクト、および強い参照を保持しているオブジェクトがガベージコレクションされなくなります。それにもかかわらず、これは間違いなく、代替手段よりも慣用的なオプションです。
別のオプション も見つかりました。
final class HttpClientImpl extends HttpClient implements Trackable {
...
// Called from the SelectorManager thread, just before exiting.
// Clears the HTTP/1.1 and HTTP/2 cache, ensuring that the connections
// that may be still lingering there are properly closed (and their
// possibly still opened SocketChannel released).
private void stop() {
// Clears HTTP/1.1 cache and close its connections
connections.stop();
// Clears HTTP/2 cache and close its connections.
client2.stop();
}
...
}
他に選択肢がない限り、これを使うのは快適ではありません。参照はおそらくタイプHttpClient
であるため、HttpClientImpl
にキャストする必要があります。 HttpClient
インターフェースではなく、将来のリリースで変更される可能性のある具体的な実装に依存するのは悪いことです。メソッドもプライベートです。 これを回避する方法 がありますが、面倒です。
明らかに、HttpClient
は自己管理するように設計されています。したがって、接続プールを維持し、ttlを単独でキャッシュする責任があります。
HttpClientCode
には、次のコードがあります。
if (!owner.isReferenced()) {
Log.logTrace("{0}: {1}",
getName(),
"HttpClient no longer referenced. Exiting...");
return;
}
これは、SelectorManager
ループを終了し、すべてのリソースをクリーンアップするための適切な方法です。
@Override
public void run() {
...
try {
while (!Thread.currentThread().isInterrupted()) {
...
if (!owner.isReferenced()) {
Log.logTrace("{0}: {1}",
getName(),
"HttpClient no longer referenced. Exiting...");
return;
}
...
}
} catch (Throwable e) {
...
} finally {
...
shutdown();
}
}
final boolean isReferenced() {
HttpClient facade = facade();
return facade != null || referenceCount() > 0;
}
したがって、HttpClient
オブジェクトが参照されない場合、すべてのリソースがクリーンアップされます。
UPD:また、タイムアウトを渡してリクエストを調整する必要があります
Java 11、各HttpClient
は、フライリクエストを処理することになっているselmgr
というデーモンスレッドを生成します。このスレッドは、ない場合は閉じられます。コード内のHttpClient
への参照。ただし、私の経験では、信頼性はありません。特に、将来のタイムアウトで非同期メソッドを使用する場合はそうです。
これは、HttpClient
を確実に閉じるためにリフレクションを使用して作成したコードです。
static void shutDownHttpClient(HttpClient httpClient)
{
ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) httpClient.executor().get();
threadPoolExecutor.shutdown();
try {
Field implField = httpClient.getClass().getDeclaredField("impl");
implField.setAccessible(true);
Object implObj = implField.get(httpClient);
Field selmgrField = implObj.getClass().getDeclaredField("selmgr");
selmgrField.setAccessible(true);
Object selmgrObj = selmgrField.get(implObj);
Method shutDownMethod = selmgrObj.getClass().getDeclaredMethod("shutdown");
shutDownMethod.setAccessible(true);
shutDownMethod.invoke(selmgrObj);
}
catch (Exception e) {
System.out.println("exception " + e.getMessage());
e.printStackTrace();
}
}
ご覧のとおり、これは実装に依存し、将来のJavaバージョンでは機能しない可能性があります。Java 11およびJava 12。
また、Javaコマンドに--add-opens Java.net.http/jdk.internal.net.http=ALL-UNNAMED
を追加する必要があります。