在 Tomcat 中使用 net.http.HttpClient 导致内存泄漏
Using net.http.HttpClient in Tomcat causes memory leak
我在基于 Servlet 的 Web 应用程序中使用 Java 的新功能(从版本 11 开始)HttpClient
:
private static final HttpClient HTTP_CLIENT =
.connectTimeout(Duration.ofSeconds(5))
.followRedirects(HttpClient.Redirect.NORMAL)
.build();
...
public void httpPostAsyncToEndpoint(WebEndpoint endpoint, Map<String,String> params) {
HttpRequest req = buildRequest(endpoint, params);
CompletableFuture<HttpResponse<String>> future = HTTP_CLIENT.sendAsync(req, HttpResponse.BodyHandlers.ofString());
future.thenAccept((HttpResponse<String> res) -> {
if (res.statusCode() >= 400) {
if (LOGGER.isErrorEnabled()) {
LOGGER.error("{} HTTP response returned from endpoint {}", endpoint, res.statusCode());
}
}
}).exceptionally(ex -> {
if (LOGGER.isErrorEnabled()) {
LOGGER.error("Could not audit event using endpoint {}", endpoint, ex);
}
return null;
});
}
一切正常,除了在 Tomcat 重新启动 Web 应用程序时会产生以下警告:
14-Aug-2020 09:21:16.996 WARNING [http-nio-8080-exec-18] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads The web application [MyApp] appears to have started a thread named [HttpClient-3-SelectorManager] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:
java.base/sun.nio.ch.WindowsSelectorImpl$SubSelector.poll0(Native Method)
java.base/sun.nio.ch.WindowsSelectorImpl$SubSelector.poll(WindowsSelectorImpl.java:357)
java.base/sun.nio.ch.WindowsSelectorImpl.doSelect(WindowsSelectorImpl.java:182)
java.base/sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:124)
java.base/sun.nio.ch.SelectorImpl.select(SelectorImpl.java:136)
java.net.http/jdk.internal.net.http.HttpClientImpl$SelectorManager.run(HttpClientImpl.java:867)
我该如何防止这种情况发生?我尝试使用自定义 ThreadFactory
,其中 returns 只有守护进程线程:
HttpClient.newBuilder()
.executor(Executors.newSingleThreadExecutor((Runnable r) -> {
Thread t = new Thread(r);
t.setDaemon(true);
return t;
}))
.connectTimeout(Duration.ofSeconds(5))
.followRedirects(HttpClient.Redirect.NORMAL).build();
但警告仍然存在。
我在 Tomcat9.
上使用 OpenJDK 11.0.7
只要对 HttpClient
的引用处于活动状态,或者只要客户端发起的操作仍在进行中,选择器管理器线程就会保持活动状态。线程可能需要几秒钟才能检测到 HttpClient
不再被引用。所以我不相信你所拥有的是真正的泄漏 - 除非持有对 HttpClient
的静态引用的 class 由于其他原因留在内存中。
我在基于 Servlet 的 Web 应用程序中使用 Java 的新功能(从版本 11 开始)HttpClient
:
private static final HttpClient HTTP_CLIENT =
.connectTimeout(Duration.ofSeconds(5))
.followRedirects(HttpClient.Redirect.NORMAL)
.build();
...
public void httpPostAsyncToEndpoint(WebEndpoint endpoint, Map<String,String> params) {
HttpRequest req = buildRequest(endpoint, params);
CompletableFuture<HttpResponse<String>> future = HTTP_CLIENT.sendAsync(req, HttpResponse.BodyHandlers.ofString());
future.thenAccept((HttpResponse<String> res) -> {
if (res.statusCode() >= 400) {
if (LOGGER.isErrorEnabled()) {
LOGGER.error("{} HTTP response returned from endpoint {}", endpoint, res.statusCode());
}
}
}).exceptionally(ex -> {
if (LOGGER.isErrorEnabled()) {
LOGGER.error("Could not audit event using endpoint {}", endpoint, ex);
}
return null;
});
}
一切正常,除了在 Tomcat 重新启动 Web 应用程序时会产生以下警告:
14-Aug-2020 09:21:16.996 WARNING [http-nio-8080-exec-18] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads The web application [MyApp] appears to have started a thread named [HttpClient-3-SelectorManager] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:
java.base/sun.nio.ch.WindowsSelectorImpl$SubSelector.poll0(Native Method)
java.base/sun.nio.ch.WindowsSelectorImpl$SubSelector.poll(WindowsSelectorImpl.java:357)
java.base/sun.nio.ch.WindowsSelectorImpl.doSelect(WindowsSelectorImpl.java:182)
java.base/sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:124)
java.base/sun.nio.ch.SelectorImpl.select(SelectorImpl.java:136)
java.net.http/jdk.internal.net.http.HttpClientImpl$SelectorManager.run(HttpClientImpl.java:867)
我该如何防止这种情况发生?我尝试使用自定义 ThreadFactory
,其中 returns 只有守护进程线程:
HttpClient.newBuilder()
.executor(Executors.newSingleThreadExecutor((Runnable r) -> {
Thread t = new Thread(r);
t.setDaemon(true);
return t;
}))
.connectTimeout(Duration.ofSeconds(5))
.followRedirects(HttpClient.Redirect.NORMAL).build();
但警告仍然存在。
我在 Tomcat9.
上使用 OpenJDK 11.0.7只要对 HttpClient
的引用处于活动状态,或者只要客户端发起的操作仍在进行中,选择器管理器线程就会保持活动状态。线程可能需要几秒钟才能检测到 HttpClient
不再被引用。所以我不相信你所拥有的是真正的泄漏 - 除非持有对 HttpClient
的静态引用的 class 由于其他原因留在内存中。