java中的四种引用——强、软、弱、虚
一、强引用
强引用比较好理解,我们编程中绝大部分对象都是强引用,在GC过程中,如果存在强引用对象,即便发生OOM,也不会被回收
Object m = new Object();
二、软引用
在堆内存不足时,jvm的GC会回收软引用对象
public class SoftReferenceTest {
public static void main(String[] args) {
SoftReference<byte[]> m = new SoftReference<>(new byte[1024 * 1024 * 10]);
System.out.println(m.get());
System.gc();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(m.get());
byte[] b = new byte[1024 * 1024 * 15];
System.out.println(m.get());
}
}
上述代码在内存中的引用关系如下图所示
假如我们指定-Xmx20M,然后运行
[B@2503dbd3
[B@2503dbd3
null
从运行结果可以看出,第一次和第二次输入对象不为空,即便中间发生了一次gc,第二次依然不为空,是因为内存足够,所以不会被回收,但是当第三次new一个15M的对象时,总空间15+10=25M,超出堆内存总大小(20M),这时jvm会回收调软引用对象。
软引用的使用场景:缓存(比如缓存一些图片文件)
三、弱引用
把上面代码稍作修改,将软引用改为弱引用,其他保持不变
public class WeakReferenceTest {
public static void main(String[] args) {
WeakReference<byte[]> m = new WeakReference<>(new byte[1024 * 1024 * 10]);
System.out.println(m.get());
System.gc();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(m.get());
byte[] b = new byte[1024 * 1024 * 15];
System.out.println(m.get());
}
}
运行结果
[B@2503dbd3
null
null
从运行结果可以看出,第二次输出时,即便内存足够,依然会被回收。
结论:垃圾回收器会直接回收弱引用对象,和软引用的区别就是不管内存是否充裕都会被回收。
ThreadLocal就是用到了弱引用技术,关于ThreadLocal可以参见另一篇博文
ThreadLocal
四、虚引用
public class PhantomReferenceTest {
private static final List<Object> LIST = new LinkedList<>();
private static final ReferenceQueue<M> QUEUE = new ReferenceQueue<>();
public static void main(String[] args) {
PhantomReference<M> pr = new PhantomReference<>(new M(), QUEUE);
//虚引用输出为空
System.out.println(pr.get());
}
}
java官方的解释:
Phantom reference objects, which are enqueued after the collector determines that their referents may otherwise be reclaimed. Phantom references are most often used for scheduling pre-mortem cleanup actions in a more flexible way than is possible with the Java finalization mechanism.
虚引用指向的对象有可能会被重新访问到,常用于对象死亡后的清理操作。
在NIO场景中,堆内存中的对象有可能会访问堆外内存的对象,当堆内存被GC回收时,堆外内存也应该释放,JVM是怎么知道堆外内存是否可回收呢,所以java提供了一种钩子机制,这种机制就是虚引用。