强引用、软引用、弱引用、虚引用以及ThreadLocal原理

一. 强引用、软引用、弱引用和虚引用
  • 强引用:使用最普遍的引用,一般情况下,垃圾回收器绝对不会回收它。内存不足时,抛出OOM。
String s = new String("hello");
s = null; //不加该行,会输出hello
System.gc(); //垃圾回收
System.out.println(s);

输出结果:null

对象只有在创建它的方法执行结束才会被回收,或者主动设置obj = null。
  • 软引用:内存空间足够,垃圾回收器不会回收它。反之,则回收。适用于缓存,而且不会OOM。
SoftReference reference = new SoftReference<>(new Object[300000000]);
System.out.println(reference.get());
Object[] objects = new Object[100000000];// 3
System.out.println(reference.get());

输出结果:
[Ljava.lang.Object;@4554617c
null

结果说明执行代码3时,内存不够,垃圾回收器主动回收了软引用指向的对象。
PS:Object数组长度根据JVM配置不同而不同。
  • 弱引用:只有当垃圾回收器扫描到弱引用指向的对象时,才会回收它。生命周期比软引用更短。ThreadLocal的key使用了弱引用。
WeakReference reference = new WeakReference<>(new String("hello"));
System.out.println(reference.get());
System.gc(); //垃圾回收
System.out.println(reference.get());

输出结果:
hello
null
  • 虚引用:在任何时候都可能被垃圾回收器回收,必须与引用队列关联使用。
ReferenceQueue queue = new ReferenceQueue<>();
PhantomReference reference = new PhantomReference<>(new String("hello"), queue);
System.out.println(reference.get());

输出结果:
null
二. ThreadLocal

一个线程可以有多个ThreadLocal实例。其作用是存储线程本地变量,独享资源,避免和主内存通信,从而提高效率。

原理:都是操作线程中的ThreadLocalMap对象。
set(obj): 获取当前线程中的ThreadLocalMap,以ThreadLocal实例为keyobjvalue,并且key是弱引用。也就是说ThreadLocal实例(未被其他变量强引用)被垃圾回收器扫描到就会被回收,从而导致key = null。这就是为什么get、set、remove的时候都会有清除Entry[]key = null的数据的步骤。

remove():会把当前key、value、key-value所在位置Entry[i]置为null,以及清除Entry[]中从i到末尾上key = null的数据。

内存泄露:key是弱引用,所以垃圾回收器会主动回收。但是valueEntry[i]都是强引用,只有线程销毁的时候才会被回收。当线程长时间未被销毁,或者线程池循环利用线程的时候,valueEntry[i]一直被强引用,不会被垃圾回收器回收,可能会造成OOM。

解决方法:使用完务必调用remove()方法,防止内存泄漏。


以上。

你可能感兴趣的:(强引用、软引用、弱引用、虚引用以及ThreadLocal原理)