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"));
去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 数组下标有一个不可释放的强引用。
下面的 threadLocalMap hash 冲突的解决方案是开发地址探测法,也就是hash重提放在临近的下一个位置。