ThreadLocal 内存溢出问题 和 java引用类型

Java 引用类型在1.2以后有4种。

 

强引用: Java中没有这个接口但是普通的赋值默认就是强引用。

  A a = new  A();这种就是强引用。

 

软引用(SoftReference):在内存紧张的时候会清理这种引用的对象

 

 

弱引用(WeakReference):任何时候都会清理这引用的种对象

 

 

虚引用(PhantomReference):几乎等同没有引用,而且通过虚引用去不到值,只能获得一些GC事件

 

GC的时候判断一个对象不可达就会回收,到一个对象有多种引用的时候,按照最强的那条算。 

 

Java  Reference 的继承图,其中 FinalReference  的子类Finalizer是JVM用的,和类的注册有关。另外几个分别对应这几种引用

 

 

 

再说说 ThreadLocal 的内存泄漏问题。

 

如果我们这么写

  ThreadLocal<String> localVar = new ThreadLocal<>();

  localVar.set(new String("abc"));


这时候建立了一个强引用。然后我们
  localVar =  null;

按理说,引用断开,不可达就会被GC回收。但是我们看看 ThreadLocal 的原理是在 Thread 里面有个ThreadLocalMap,然后用localVar 作为key,来存数据的。








ThreadLocal 获取值得源码




去Thread里面取Map 对象

 

Thread 对象里面 threadLocals 这个Map 对象

 

 

Thread 对象里面的成员变量 threadLocals这个map对象 里面存着 Entry,Entry 里面放着引用分别指向 ThreadLocal(也就是例子定义的 localVar),和 ThreadLocal 的值("abc")

 

我们看看引用关系
1. localVar引用 指向 localVar对象,强引用
2. threadLocals 有个Entry[] table数组,里面某下标存在Entry。table指定下标到 Entry 强引用。

3. Entry 里面放着 value,并且entry的key是存在父类 WeakReference 里面的。key指向localVar对象值,这里是弱引用,value指向上面设置的“abc”,成员变量是一个强引用。

4. 这时候指向 localVar对象值的引用有两个,一个是key 指向它的弱引用,另外一个是 localVar指向它的强引用。localVar对象不会被回收。

5. 当第一条里面的localVar = null以后,localVar对象的强引用断掉,localVar对象只有 key 这个弱引用指向它。localVar就会在内存不够的时候被回收。

6. 在看看value的情况 指向value 值的引用是 value这个成员变量,强引用。指向Entry对象的引用是  threadLocalMap 里面 个Entry[] table的某个下标,也是强引用。这个强引用不断开value 永远不会被回收。

 

如果不停的定义新的ThreadLocal变量,然后就会因为value的内存没有被释放内存溢出。

这个问题怎么解决呢?我们只要然这个Thread 成员变量数组里面对应 entry的下标指向null就行了。threadLocals[n] = null;

ThreadLocal 的remove就做了类似的处理。
所以我们应该这样写
  localVar.remove();
  localVar= null;

 

总结:弱引用解决了Threadlocal 部分的内存溢出问题,但是没有完全解决,并不是因为弱引用才出的内存溢出问题,而是在 ThreadLocalMap 数组下标有一个不可释放的强引用。


Entry结构,里面key是弱引用,value是一个成员变量强引用。

 

 

remove清除数据下边的做法,大概逻辑就是 调用entry.clear(),然后tab[staleSlot] = null;

下面的 threadLocalMap hash 冲突的解决方案是开发地址探测法,也就是hash重提放在临近的下一个位置。



 

posted on 2022-10-13 19:20  zhangyukun  阅读(191)  评论(0编辑  收藏  举报

导航