强软弱虚引用以及 ThreadLocal 的原理和内存泄露问题

四大引用


强引用

特点:只有对象没有被引用的时候,才会被回收。

示例:Object o =new Object();


软引用

特点:内存不够的时候,软引用就会被GC掉

实例:SoftReference o1 = new SoftReference<>(new Object());

使用场景:缓存


弱引用

特点:系统只要GC,就会被回收掉

实例:WeakReference o2 = new WeakReference<>(new Object());

使用场景:ThreadLocal 中就有用到


虚引用

特点:用来管理直接内存的,它无法被捕获到,它被回收的时候,会给出一个信号,然后回收它的直接内存。

实例:

ReferenceQueue<Object> referenceQueue = new ReferenceQueue<Object>();

// 创建虚引用需要创建一个队列,用于接收被回收的对象
PhantomReference<Object> o3 
	= new PhantomReference<Object>(new Object(),referenceQueue);
// 返回的是null
System.out.println(o3.get());

使用场景:对直接内存的使用和管理



ThreadLocal

此图做参考:

强软弱虚引用以及 ThreadLocal 的原理和内存泄露问题_第1张图片

其实 ThreadLocal 其实只是一个引子,能保证每个线程的数据访问隔离的其实是:每个线程维护了一个ThreaLocals(map)来存储每个线程对象的变量值。每个线程对象私有属性值,可不是互相隔离嘛。

而这个map,不是用的现成的Map,而是thread的一个静态内部类,它存储的形式和咱们常用的hashmap类似,都是一个 Entry 的形式,而 Entry 的 key 和 value 分别是:
key:ThreadLocal对象的弱引用(默认1.5以后,之前是ThreadId作为key)
value:线程的变量

核心源码:

// Entry的组成和弱引用的使用
static class Entry extends WeakReference<ThreadLocal<?>> {
     
    /** The value associated with this ThreadLocal. */
    Object value;
	// 继承父类的构造方法
    Entry(ThreadLocal<?> k, Object v) {
     
        super(k);
        value = v;
    }
}

// set方法
public void set(T value) {
     
    Thread t = Thread.currentThread();// 获取到当前线程
    ThreadLocalMap map = getMap(t);// 获取到现成对应的map
    if (map != null) {
     
        map.set(this, value); 
    } else {
     
        createMap(t, value);
    }
}

其他代码,大家可以自己去看一下,很简单!



为什么用ThreadLocal的弱引用做key,而不是直接使用前引用的呢?


注:T1是一个ThreadLocal对象

根据之前给大家的图,如果是强引用的话,如果这个 T1 被一个线程set值之后,只要这个线程不结束,那么 ThreadLocalMapT1 的引用就一直存在, T1 就永远不会被GC。如果这个线程是一个长久执行的线程,但是这个 T1 只是线程初始化的时候引用一次,此时就会造成内存泄露。

因为弱引用的特点就是:只要GC,就会被回收。

如果此时 ThreadLocalMapT1 是弱引用,当线程对 T1 使用结束之后,只要 把 T1 设置为 null ,下次 GC 的时候,T1 对应的那块内存将直接被回收掉。


这样就不会内存泄露的吗?

答案是否定的。

如果 T1 会回收之后,ThreadLocalMap 中 T1 对应的 key 就变成了 null ,此时 T1 对应 value 长时间没有被回收,也会造成内存溢出,所以我们在不用 ThreadLocal 中值的时候,需要先手动 remove 一下。

原因:虽然我们每次 对 ThreadLocalMap 进行 getset 的时候,方法都会进行 null 值的判断,如果有 keynull的,就会删除,但是如果这个线程很长时间不执行 get set 的话,同样会造成内存泄露的问题。



简单总结,如有纰漏欢迎指出交流,感觉不错就点个赞呗 在这里插入图片描述

你可能感兴趣的:(java,java,多线程)