使用Tomcat中的net.http.HttpClient会导致内存泄漏。

huangapple go评论85阅读模式
英文:

Using net.http.HttpClient in Tomcat causes memory leak

问题

我在一个基于Servlet的Web应用中使用Java新版本(自版本11以来)的HttpClient,一切都运行得很顺利,除了在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 仅返回守护线程:

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();

但警告仍然存在。

我正在使用OpenJDK 11.0.7和Tomcat 9。

如果还有其他问题,请告诉我。

英文:

I'm using the Java's new (since version 11) HttpClient within a Servlet based web application:

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;
  });
}

Everything is working great, except that when the web application is restarted on Tomcat the following warning is produced:

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)

How can I prevent this? I've attempted to use a custom ThreadFactory which returns only daemon threads:

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();

but the warning persists.

I'm using OpenJDK 11.0.7 on Tomcat 9.

答案1

得分: 3

选择器管理线程将保持活动状态,只要对 HttpClient 的引用存在 - 或者只要客户端发起的操作仍在进行中。线程可能需要几秒钟的时间来检测到 HttpClient 不再被引用。因此,我认为您拥有的情况不是真正的泄漏 - 除非保存静态引用到 HttpClient 的类出于其他原因而在内存中保持固定。

英文:

The selector manager thread will remain alive as long as the reference to the HttpClient is alive - or as long as an operation initiated by the client is still in progress. It may take a couple of seconds for the thread to detect that the HttpClient is no longer referenced. So I don't believe that what you have is an actual leak - unless the class that held the static reference to the HttpClient stays pinned in memory for other reasons.

huangapple
  • 本文由 发表于 2020年8月14日 23:42:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/63415935.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定