Java中的强引用、软引用、弱引用、虚引用
前言
学过Java的同学可定听说过Java中的强引用、软引用、弱引用、虚引用,那么这四种引用再Java开发中有什么作用呢?引用之间又有什么区别?
正文
1. 强引用(StrongReference)
强引用是我们平常使用最多的引用,它的特性是如果一个对象具有强引用,那么垃圾回收器绝不会回收它。当内存空间不足时,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。
Object o = new Object() // 类似于这样的都是强引用
当我们需要垃圾回收器回收这个对象时,我们可以通过一下方法:
o = null;
此时Object()就没有引用指向它,当垃圾回收器执行任务时,Object()就会被回收。
在一个方法的内部有一个强引用,这个引用保存在Java栈中,而真正的引用内容(Object)保存在Java堆中。 当这个方法运行完成后,就会退出方法栈,则引用对象的引用数为0
,这个对象会被回收。
但是如果强引用的对象是全局变量时,那么就需要在不用时将对象赋值为null,因为强引用不会被垃圾回收。
2. 软引用(SoftReference)
软引用的特性是指:当一个对象具有软引用时,如果在内存充足的情况下,垃圾回收器是不会回收它的。但是如果内存不充足,这个时候当垃圾回收器执行任务时,就会将软引用的对象回收掉。
SoftReference<byte[]> sr = new SoftReference<>(new byte[1024*1024*10]); // 定义一个10m空间的byte数组
这里需要强调一点:
sr = new SoftReference<>();
这个引用是强引用的,只不过在这个对象里面软引用了一个byte数组。
我们通过下面的一段代码来看看软引用的特性是否正确:
在运行环境前我们先在VM options设置堆内存大小为20m
-Xmx20m
public class Main {
public static void main(String[] args) {
// 定义一个10m空间的byte数组,用软引用
SoftReference<byte[]> sr = new SoftReference<>(new byte[1024 * 1024 * 10]);
// 使用get()方法可以得到软引用对象
System.out.println(sr.get());
// 进行垃圾回收
System.gc();
// 延时等待
try {
Thread.sleep(3_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 再次打印软引用对象
System.out.println(sr.get());
// 定义一个12m对象
byte[] b = new byte[1024 * 1024 * 12];
// 再次打印软引用对象
System.out.println(sr.get());
}
}
结果:
[B@1b6d3586
[B@1b6d3586
null
当加入一个12m的byte数组时,因为此时堆内存中已经有了个10m的数组,而堆内存大小为20m,堆内存空间不够,这就导致了软引用对象的释放。因此第三次打印时显示为null。
了解了什么是软引用,那么它的作用是什么呢? 其实软引用的作用一般用来实现内存敏感的高速缓存。例如:我们访问一张图片时,就可以用软引用指向它。这样的好处是,当我们下次访问时就可以快速的拿到这张图片,并且当内存空间不足时可以立马释放掉。这样既可以快速访问,又可以自动释放掉,起到了很好的缓存作用。
3. 弱引用(WeakReference)
弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。
为了验证这一特性,直接上代码:
public class Main {
public static void main(String[] args) {
// 定义一个10m空间的byte数组,用弱引用
WeakReference<byte[]> wr = new WeakReference<>(new byte[1024 * 1024]);
// 打印一下
System.out.println(wr.get());
// 进行垃圾回收
System.gc();
// 延时等待
try {
Thread.sleep(3_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 再次打印一下
System.out.println(wr.get());
}
}
结果:
[B@1b6d3586
null
结果很明显,当一次垃圾回收器执行任务后,弱引用对象就被清空了。
那么弱引用一般用于哪里呢?其实,在ThreadLocal中就使用到了弱引用,可以去了解了解。
4. 虚引用(PhantomReference)
虚引用一般我们日常开发中用的不多,它的特性是无法get()到虚引用对象,垃圾回收器直接回收。
一般使用在Java内存引用系统内存,或者用来跟踪对象被垃圾回收器回收的活动。
虚引用主要用来跟踪对象被垃圾回收器回收的活动。 虚引用与软引用和弱引用的一个区别在于:
虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。
程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要进行垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。
总结
引用类型 | 被垃圾回收时间 | 用途 | 生存时间 |
---|---|---|---|
强引用 | 从来不会 | 对象的一般状态 | JVM停止运行时终止 |
软引用 | 当内存不足时 | 对象缓存 | 内存不足时终止 |
弱引用 | 正常垃圾回收时 | 对象缓存 | 垃圾回收后终止 |
虚引用 | 正常垃圾回收时 | 跟踪对象的垃圾回收 | 垃圾回收后终止 |
引用: