是否值得清除Filter中的ThreadLocals以解决与线程池相关的问题?
简而言之 – tomcat使用线程池,因此线程可以重用。 有些库使用ThreadLocal
变量,但不清理它们(使用.remove()
),因此实际上它们会将“脏”线程返回到池中。
Tomcat具有在关闭时检测这些内容以及清除线程本地的新function。 但这意味着线程在整个执行过程中都是“脏”的。
我可以做的是实现一个Filter
,并在请求完成后(并将线程返回到池中),使用tomcat中的代码 (称为checkThreadLocalsForLeaks
的方法)清理所有ThreadLocal
。
问题是,值得吗? 两个优点:
- 防止内存泄漏
- 防止库的不确定行为,假设线程是“新鲜的”
一个骗局:
- 该解决方案使用reflection,因此它可能很慢。 当然,所有reflection数据(
Field
s)都将被缓存,但仍然如此。
另一种选择是将问题报告给不清除其线程本地的库。
我会通过以下两个原因通过向图书馆开发人员报告问题的途径:
- 它将有助于其他想要使用相同库的人,但缺乏技能/时间来找到这样一个可怕的内存泄漏。
- 帮助库的开发人员构建更好的产品。
老实说,我以前从未见过这种类型的错误,我认为这是一个例外,而不是我们应该保护的事情,因为它经常发生。 你能分享一下你见过这种行为的图书馆吗?
作为旁注,我不介意在开发/测试环境中启用该filter,并且如果仍附加ThreadLocal变量则记录严重错误。
从理论上讲,这似乎是一个好主意。 但是,我可以看到一些你可能不想这样做的情况。 例如,一些与xml相关的技术有一些非常重要的设置成本(比如设置DocumentBuilders和Transformers)。 如果你在webapp中做了很多这样的事情 ,那么在ThreadLocals中缓存这些实例可能是有意义的(因为实用程序通常不是线程安全的)。 在这种情况下,您可能不希望在请求之间清除这些。
如果您认为线程的肮脏可能会导致问题,那么这是一件明智的事情。 在可能的情况下应避免出现问题。
使用threadlocals可能是库的不良行为,你当然应该向作者报告,但遗憾的是,现在,由你来处理它。
我不会太担心性能。 reflection中的慢速位是元数据查找; 一旦你有了一个Field对象,那么使用它是相当快的,并且随着时间的推移变得更快 – AIUI,它开始通过对JVM进行本机调用来工作,但经过一些使用后,它会为访问生成字节码,然后可以编译成本机代码,优化,内联等,因此它不应该比直接字段访问慢得多。 我不认为Tomcat代码会跨请求重用Field对象,所以如果你想利用它,你必须编写自己的清理代码。 在任何情况下,性能成本将远远小于与请求关联的IO的成本。