线程池中的ThreadLocal内存泄漏问题

4
我在Tomcat中使用ThreadPool,但没有在我的Web应用程序中实现ThreadLocal,却遇到了threadlocal内存泄漏错误。
严重: Web应用程序[/myWebApp]创建了一个类型为[org.apache.http.impl.cookie.DateUtils$DateFormatHolder$1](值为[org.apache.http.impl.cookie.DateUtils$DateFormatHolder$1@4c2849])的键ThreadLocal,并且值类型为[java.lang.ref.SoftReference](值为[java.lang.ref.SoftReference@1e67280]),但在停止Web应用程序时未能删除它。线程将随时间而更新,以尝试避免可能的内存泄漏。
我不明白为什么会出现threadlocal错误,尽管我没有实现它?我想摆脱这些消息,所以我在网上搜索,在这里写道,为了清除threadlocal,我需要使用:
ThreadLocal.remove()

但是我没有ThreadLocal的实现.. 如果有人能向我展示一种方法,我将不胜感激。


你可能在你的应用程序中创建了线程。当servlet关闭/重新启动时,你是否尝试过销毁/清理它们?在JEE环境中通常不建议自己启动线程,而是使用像Quartz这样的框架。 - Guillaume Polet
1
是的,我正在创建线程,但我将它们添加到池中。并且在完成后,我关闭了线程池执行器。 - anvarik
4个回答

2
显然,有些东西正在创建那些ThreadLocal实例。如果这不是你的代码,那么它一定是你正在使用的某个库或(不太可能的情况)Tomcat本身。
我建议首先查看可能正在创建实例的内容。
    org.apache.http.impl.cookie.DateUtils$DateFormatHolder$1
< p> 顺便说一下,在DataUtils的嵌套类中有一个匿名类...所以除非发生了一些奇怪的事情,否则创建将在DateUtils.java文件中发生。

如果检查源代码没有帮助,请尝试调试Tomcat实例,并在ThreadLocal构造函数上设置断点。


2
这里是HttpClient JIRA:https://issues.apache.org/jira/browse/HTTPCLIENT-1216 自从4.2.2版本开始,有一个clearThreadLocal()方法;从4.3版本开始,cookie-DateUtils已经被弃用并替换为org.apache.http.client.utils.DateUtils。
仅在关闭时调用一次DateUtils.clearThreadLocal()是不够的,它只清除当前线程的ThreadLocal,所以您需要在执行解析/格式化日期的HTTP请求后,在该线程上调用它。但这会消除使用ThreadLocal的大部分性能优势。
或者,如果您从一个受您控制的线程(而不是由Tomcat创建的线程)执行HTTP请求,请记得在应用程序关闭时关闭任何线程池/执行程序。
遗憾的是,HttpClient很容易被修改以不覆盖ThreadLocal,那么ThreadLocal就不会引用Web应用程序及其类加载器,从而避免了大部分泄漏问题:(

更新:据说在httpclient 4.5.x之后的4.5.1中已经修复。关于避免覆盖ThreadLocal,自Java 8以来,有ThreadLocal.withInitial(Supplier)方法,但它会引用您的Supplier实现,进而会拉取Web应用程序的类加载器和“所有内容”。 - Stefan L

2

我想要补充一下,这不是因为你正在运行自己的线程池,而是因为第三方正在污染Tomcat运行所有传入HTTP请求的“本地”线程池。 - jontro

2

很明显,ThreadLocal是由你使用的某个框架或库创建的(查看哪个在使用HttpClient),但是正如您在日志中看到的那样,该值是一个软引用,应该可以最小化内存泄漏。

实际上,您可以在DateUtils代码中看到它正在创建Threadlocal......


1
虽然SoftReference可以减少内存泄漏,但无法完全解决问题。ThreadLocal现在与类加载器绑定,因此热部署可能导致永久代内存溢出。 - John Vint

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接