ThreadLocal为什么不使用Thread-value实现

实现

各个Thread对象保存一个ThreadLocalMap<ThreadLocal,value>,保存的是本Thread涉及的所有的ThreadLocal变量的本线程版本数据。

一个app中可能多出用到不同的ThreadLocal,tl1被t1 t3使用,tl2被t1,t2,t4使用,所以t1的Map中保存了两个entry。

为什么不是的Map<Thread,Value>的实现

假如一个ThreadLocal对象是一个Thread-Value简单map,那么需要保证这个map本身并发安全。已经知道HashMap在扩容时会发生死循环。

优点

  • 并发程度

所以,现在是在实现时,真的把ThreadLocal代表的语义变量设置到了线程局部变——Thread对象的私有数据中。
而如果使用Map<Thread,Value>实现,实际上还是使用共享的结构控制模拟出局部的效果。需要同步控制。

  • 内存泄露

Entry是弱引用,所以当tl对象本身可以回收的时候,Entry不会阻止tl被回收。
但是如果不正确remove就会导致内存泄露,这个不是ThreadLocal的缺点,但是是使用者需要注意的一个要点。

当不remove的时候,真正的value是被强引用的,只要thread还runnable,那么Thread对象不会回收,其引用的Map也不会回收,map引用的Entry对象也不会回收,只不过Entry对key的引用是弱引用,所以key可以回收,但是entry对value的应用是强引用,value会被阻止回收。——所以对于service性质的线程如果使用ThreadLocal而没有remove,那么就会一直保持泄露,虽然一个value不多,但是如果不断使用新的ThreadLocal对象,就会不断泄露。

Tomcat的PermGen泄露。

前提还是没有remove,因为Tomcat是使用线程池调用servlet,线程一直保持ThreadLocal对象的当前线程版本,忧郁真正的value可能是WebAppClassloader加载的class类型,那么value引用了Class对象,Class对象应用了WebAppClassloader对象,他们都不会被回收,那么webappclassloader对象加载的所有class文件在永久代占用的内存一直无法被回收。——一个webapp是一些class构成的,正常情况reload这个app的时候,如果webappClassloader对象不被引用就可以回收,那么就不会阻止方法区的空间回收。但是由于ThreadLocal导致的一个value对象的泄露导致一大片的方法区无法回收,reload几次的话就可能导致PermGen oom。

tomcat webapp stop时应清理自己

使用的ThreadLocal至少在webapp stop之前要remove。
另外从tomcat的log中可以看出几点要点提示。
SEVERE: The web application registered the JDBC driver [oracle.jdbc.OracleDriver] but failed to unregister it when the web application was stopped. To prevent a memory leak, the JDBC Driver has been forcibly unregistered.

org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
SEVERE: The web application appears to have started a thread named [Thread-10] but has failed to stop it. This is very likely to create a memory leak.

and

SEVERE: The web application created a ThreadLocal with key of type [java.lang.ThreadLocal] (value [java.lang.ThreadLocal@64b43\712c]) and a v
alue of type [org.apache.logging.log4j.core.async.AsyncLogger.Info] (value [org.apache.logging.log4j.core.async.AsyncLogger$Info@3404e643e]) but failed
to remove it when the web application was stopped. This is very likely to create a memory leak.

Read more: https://javarevisited.blogspot.com/2012/01/tomcat-javalangoutofmemoryerror-permgen.html#ixzz5kyBlAh6J

posted on 2019-03-11 12:43  还好可以改名字  阅读(823)  评论(0编辑  收藏  举报