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

 

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

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

 

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

 

 

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

 

 

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

 

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

 

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

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

 

 

 

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

 

如果我们这么写

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

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

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

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








ThreadLocal 获取值得源码
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

去Thread里面取Map 对象

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

 

Thread 对象里面 threadLocals 这个Map 对象

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

 

 

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是一个成员变量强引用。
  • 1.

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

 

 

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

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

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



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