如何清理threadlocals

有没有人有一个例子如何做到这一点? 还是由垃圾收集器处理? 即时通讯使用tomcat6

javadoc这样说:

“只要线程处于活动状态并且ThreadLocal实例是可访问的,每个线程就拥有对其线程局部variables的副本的隐式引用;在线程消失后,线程本地实例的所有副本都将进行垃圾回收(除非存在对这些副本的其他引用)。

如果您的应用程序或(如果您正在讨论请求线程)容器使用线程池,这意味着线程不会死亡。 如果有必要,你将需要自己处理线程本地人。 唯一干净的方法是调用ThreadLocal.remove()方法。

有两个原因可能需要清理线程池中线程的线程局部variables:

  • 以防止内存(或假设资源)泄漏,或
  • 以防止通过线程本地人将信息从一个请求意外泄漏到另一个请求。

线程局部内存泄漏通常不应该是有界线程池的主要问题,因为任何线程局部最终都可能被覆盖; 即当线程被重用时。 但是,如果你ThreadLocal创build一个新的ThreadLocal实例(而不是使用一个staticvariables来保存单例实例)的错误,线程本地值将不会被覆盖,并将积累在每个线程的threadlocals映射中。 这可能会导致严重的泄漏。


假设您正在讨论在Web应用程序处理HTTP请求期间创build/使用的线程本地,那么避免线程本地泄漏的一种方法是使用您的requestDestroyed应用程序的ServletContext注册ServletRequestListener ,并实现侦听器的requestDestroyed方法来清理线程当前线程的当地人。

请注意,在这种情况下,您还需要考虑从一个请求泄漏到另一个请求的可能性。

这里有一些代码来清除当前线程的所有线程局部variables,当你没有一个实际的线程局部variables的引用。 您也可以将其推广到清理其他线程的线程局部variables:

  private void cleanThreadLocals() { try { // Get a reference to the thread locals table of the current thread Thread thread = Thread.currentThread(); Field threadLocalsField = Thread.class.getDeclaredField("threadLocals"); threadLocalsField.setAccessible(true); Object threadLocalTable = threadLocalsField.get(thread); // Get a reference to the array holding the thread local variables inside the // ThreadLocalMap of the current thread Class threadLocalMapClass = Class.forName("java.lang.ThreadLocal$ThreadLocalMap"); Field tableField = threadLocalMapClass.getDeclaredField("table"); tableField.setAccessible(true); Object table = tableField.get(threadLocalTable); // The key to the ThreadLocalMap is a WeakReference object. The referent field of this object // is a reference to the actual ThreadLocal variable Field referentField = Reference.class.getDeclaredField("referent"); referentField.setAccessible(true); for (int i=0; i < Array.getLength(table); i++) { // Each entry in the table array of ThreadLocalMap is an Entry object // representing the thread local reference and its value Object entry = Array.get(table, i); if (entry != null) { // Get a reference to the thread local object and remove it from the table ThreadLocal threadLocal = (ThreadLocal)referentField.get(entry); threadLocal.remove(); } } } catch(Exception e) { // We will tolerate an exception here and just log it throw new IllegalStateException(e); } } 

没有办法清除ThreadLocal值,除非是在线程放置它们 (或者线程被垃圾回收 – 而不是工作线程的情况)。 这意味着当Servlet请求完成时(或者在将AsyncContext传送到Servlet 3中的另一个线程之前),应该注意清理你的ThreadLocal,因为在那之后你可能永远不会有机会进入特定的工作线程,将在您的Web应用程序未部署而服务器未重新启动的情况下泄漏内存。

ServletRequestListener.requestDestroyed()是做这种清理的好地方。

如果你使用Spring,所有必要的布线已经到位,你可以简单地把东西放在你的请求范围内,而不用担心清理它们(自动发生):

 RequestContextHolder.getRequestAttributes().setAttribute("myAttr", myAttr, RequestAttributes.SCOPE_REQUEST); . . . RequestContextHolder.getRequestAttributes().getAttribute("myAttr", RequestAttributes.SCOPE_REQUEST); 

即使年龄大了,我也想回答这个问题。 我一直困扰着同样的问题(gson threadlocal没有从请求线程中移除),甚至在服务器耗尽内存的情况下重启服务器(这很糟糕!)。

在设置为开发模式的java web应用程序的上下文中(在服务器被设置为每次感知代码中的变化时弹跳,并且可能还在debugging模式下运行)的情况下,我很快知道threadlocals可以是真棒的有时候会很痛苦 我为每个请求使用threadlocal调用。 在调用内部。 我有时也会使用gson来产生我的回应。 我将在filter的“try”块中包装Invocation,并在“finally”块中将其销毁。

我观察到的(我现在还没有指标来支持)是,如果我对多个文件进行了更改,并且服务器在我的更改之间不停地跳动,我会变得不耐烦,重新启动服务器(精确地说,tomcat)从IDE。 最有可能的是,我最终会遇到“内存不足”的例外。

我如何解决这个问题是在我的应用程序中包含一个ServletRequestListener实现,我的问题消失了。 我认为发生了什么事是在请求的中间,如果服务器会弹跳几次,我的threadlocals没有得到清理(包括gson),所以我会得到这个关于threadlocals警告和两个或三个警告后,服务器会崩溃。 用ServletResponseListener明确地closures我的threadlocals,gson问题就消失了。

我希望这是有道理的,并给你一个如何克服threadlocal问题的想法。 始终closures他们的使用点。 在ServletRequestListener中,testing每个线程本地包装器,如果它仍然有对某个对象的有效引用,那么在那个时候将其销毁。

我也应该指出,把一个threadlocal作为一个静态variables包装在一个类中是一种习惯。 这样,你可以保证通过在ServeltRequestListener中销毁它,你不必担心同一个类的其他实例在四处闲逛。

JVM会自动清理ThreadLocal对象内的所有无引用对象。

清理这些对象的另一种方式(比如说,这些对象可能是所有线程中不安全的对象)是把它们放到一些Object Holder类里面,这个类基本上就是这样,你可以重载finalize方法来清理对象它驻留在它内部。 这又取决于垃圾收集器及其策略,何时会调用finalize方法。

这是一个代码示例:

 public class MyObjectHolder { private MyObject myObject; public MyObjectHolder(MyObject myObj) { myObject = myObj; } public MyObject getMyObject() { return myObject; } protected void finalize() throws Throwable { myObject.cleanItUp(); } } public class SomeOtherClass { static ThreadLocal<MyObjectHolder> threadLocal = new ThreadLocal<MyObjectHolder>(); . . . } 

仔细阅读Javadoc文档:

'只要线程处于活动状态并且ThreadLocal实例是可访问的,每个线程就拥有对其线程局部variables副本的隐式引用; 在一个线程消失之后,线程本地实例的所有副本都会被垃圾收集(除非存在对这些副本的其他引用)。 “

没有必要清理任何东西,泄漏的生存条件是“和”的。 因此,即使在一个Web容器中,线程可以存活到应用程序中,只要webapp类被卸载(只有在父类加载器中加载的静态类中的引用才能防止这种情况,这与ThreadLocal没有任何关系,与静态数据共享jar),那么AND条件的第二条腿就不再满足,所以线程本地拷贝有资格进行垃圾收集。

线程本地不能成为内存泄漏的原因,只要实现符合文档。