一. 强引用、软引用、弱引用和虚引用
- 强引用:使用最普遍的引用,一般情况下,垃圾回收器绝对不会回收它。内存不足时,抛出OOM。
String s = new String("hello");
s = null; //不加该行,会输出hello
System.gc(); //垃圾回收
System.out.println(s);
输出结果:null
对象只有在创建它的方法执行结束才会被回收,或者主动设置obj = null。
- 软引用:内存空间足够,垃圾回收器不会回收它。反之,则回收。适用于缓存,而且不会OOM。
SoftReference<Object[]> 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<String> reference = new WeakReference<>(new String("hello"));
System.out.println(reference.get());
System.gc(); //垃圾回收
System.out.println(reference.get());
输出结果:
hello
null
- 虚引用:在任何时候都可能被垃圾回收器回收,必须与引用队列关联使用。
ReferenceQueue<String> queue = new ReferenceQueue<>();
PhantomReference<String> reference = new PhantomReference<>(new String("hello"), queue);
System.out.println(reference.get());
输出结果:
null
二. ThreadLocal
一个线程可以有多个ThreadLocal
实例。其作用是存储线程本地变量,独享资源,避免和主内存通信,从而提高效率。
原理:都是操作线程中的ThreadLocalMap
对象。
set(obj)
: 获取当前线程中的ThreadLocalMap
,以ThreadLocal
实例为key
,obj
为value
,并且key
是弱引用。也就是说ThreadLocal
实例(未被其他变量强引用)被垃圾回收器扫描到就会被回收,从而导致key = null
。这就是为什么get、set、remove
的时候都会有清除Entry[]
中key = null
的数据的步骤。
remove()
:会把当前key、value、key-value
所在位置Entry[i]
置为null
,以及清除Entry[]
中从i
到末尾上key = null
的数据。
内存泄露:key
是弱引用,所以垃圾回收器会主动回收。但是value
和Entry[i]
都是强引用,只有线程销毁的时候才会被回收。当线程长时间未被销毁,或者线程池循环利用线程的时候,value
和Entry[i]
一直被强引用,不会被垃圾回收器回收,可能会造成OOM。
解决方法:使用完务必调用remove()
方法,防止内存泄漏。
以上。