ThreadLocalMap-Entry的key是弱引用 value是强引用
ThreadLocal内存溢出问题
内存泄露的本质是Entry不能被回收,
避免内存泄露的方法:
(1)是否手动删除了Entry;使用完ThreadLocal,调用其remove方法删除对应的Entry,就能避免内存泄漏
(2)CurrentThread有没被关闭;由于ThreadLocalMap是Thread的一个属性,被当前线程所引用,所以ThreadLocalMap的生命周期跟Thread一样长。所以,在使用完ThreadLcoal后当前Thread也随之结束,ThreadLocalMap也就自然会被GC回收,避免了内存泄漏
Entry的key是弱引用 value是强引用
1.ThreadLocalMap中的Key为什么使用弱引用
key如果使用强引用 如果没有主动删除Entry 那么只有线程结束的时候ThreadLocal才会被回收
ThreadLocal内存溢出问题
但是有一种危险是,如果线程是线程池的,在线程执行完代码的时候并没有结束,只是归还给线程池,这个时候ThreadLocalMap和里面的元素是不会回收掉的。
在ThreadLocalMap中的set/getEntry中,会对Key = NULL(即ThreadLocal = NULL)的进行判定,如果为NULL,将会把value也置为NULL。
这就意味着:使用完Thread Local,CurrentThread仍然运行的情况下,就算忘记remove(),弱引用也会比强引用多一层保障。
2.Value为什么是强引用
这里假设value和threadlocal都是弱引用,假如threadlocal除了 被Entry这个弱引用所引用之外,还被强引用(使用threadLocal时threadLocal对象肯定是被强引用的,当不被使用时也就没被强引用这时候Gc可回收),但这时threadLocal.set(value),value的值没被其它对象引用,只是传递给thread,也就是value 这时只被Entry这个弱引用所引用,这时候发生gc,threadlocal不会被回收,value回被回收 ,导致通过threadlocal获得value值时获得为Null。
3.Value存在内存泄露
Value的惰性删除
我们知道expungeStaleEntry() 方法是帮助垃圾回收的,根据源码,我们可以发现 get 和set 方法都可能触发清理方法expungeStaleEntry(),所以正常情况下是不会有内存溢出的。
但是如果我们没有调用get和set的时候就会可能面临着内存溢出。
所以不再使用的时候调用remove(),加快垃圾回收,避免内存溢出。
4.ThreadLocal对象一直存在时一定要remove()
假设Controller 对应的serviceImpl 有一个ThreadLocal对象
ThreadLocal对象 spring生命周期 保证应用重启前他一直存在的
所以Entry的key不为空 Entry一直存在 get()/set()是不能清除value的 ,Value是强引用 所以有可能造成:
1)内存溢出
2)第二次调用同一个线程 会有数据污染
所以用完就删除