引用的类型
在Java中提供了4个级别的引用:强引用、软引用、弱引用和虚引用。之所以整出这么多的引用类型,是因为引用也有轻重缓急,称做为强软弱虚,分门别类有助于GC更好的进行垃圾回收。下面我们就来具体看下这些引用类型。
public class Reference {
@Override
protected void finalize() throws Throwable {
System.out.println("finalize");
}
}
之所以重写finalize
也仅仅是用于此次的测试,一旦Reference
对象被回收,finalize
方法就会被调用。
Caution
在实际应用中,这个方法永远不要去重写。
强引用
强引用就是程序中一般使用的引用类型,强引用的对象是可触及的,不会被回收。下面是一个强引用的例子
public class ReferenceMain {
public static void main(String[] args) throws IOException {
var reference = new Reference();
reference = null;
System.gc(); // 显示的进行垃圾回收
System.in.read(); // 只是阻塞当前线程, GC是在另外一个线程执行
}
}
强引用具备以下特点:
- 强引用可以直接访问目标对象
- 强引用所指向的对象在任何时候都不会被系统回收,虚拟机宁愿抛出OOM异常,也不会回收强引用所指向的对象
- 强引用可能导致内存泄露
软引用
如果一个对象只持有软引用,那么当堆空间不足时,就会被回收。软引用使用java.lang.ref.SoftReference
类实现。
public class ReferenceMain {
public static void main(String[] args) throws IOException, InterruptedException {
// 在堆上面分配10M的内存
var softReference = new SoftReference<>(new byte[1024 * 1024 * 10]);
System.out.println(softReference.get());
System.gc();
TimeUnit.SECONDS.sleep(1);
System.out.println(softReference.get());
// 再分配一个15M的数组,堆(20M)将装不下。这时候系统垃圾回收器先回收一次,如果不够,会将软引用回收掉
byte[] bytes = new byte[1024 * 1024 * 15];
System.out.println(softReference.get());
}
}
运行时,修改JVM参数-Xms20M -Xmx20M
,设置堆内存为固定大小20M。有如下结果
[B@372f7a8d
[B@372f7a8d
null
Info
使用场景:图片缓存
图片缓存框架中,内存缓存的图片是以软引用这种方式保存,使得 JVM 在发生 OOM 之前,可以回收这部分缓存。此外还可以用在网页缓存上。
弱引用
在系统GC时,只要发现弱引用,不管系统堆空间使用情况如何,都会将对象进行回收。但是,由于垃圾回收器的线程通常优先级很低,并不一定能很快地发现持有弱引用的对象。在这种情况下,弱引用对象可以存在较长的时间。
一旦一个弱引用对象被垃圾回收器回收,便会加入一个注册的引用队列。弱引用使用java.lang.ref.WeakReference
类实现。
public class ReferenceMain {
public static void main(String[] args) throws IOException, InterruptedException {
var weakReference = new WeakReference<>(new Reference());
System.out.println(weakReference.get());
System.gc();
System.out.println(weakReference.get());
var t1 = ThreadLocal.withInitial(Reference::new);
t1.remove();
}
}
输出结果
com.huhx.thread.reference.Reference@2f92e0f4
null
finalize
使用场景:容器WeakHashMap
在静态内部类中,经常会使用虚引用。例如:一个类发送网络请求,承担 callback 的静态内部类,则常以虚引用的方式来保存外部类的引用,当外部类需要被 JVM 回收时,不会因为网络请求没有及时回应,引起内存泄漏。
虚引用
虚引用是所有引用类型中最弱的一个。一个持有虚引用的对象,和没有引用几乎是一样的,随时都可能被垃圾回收器回收。当试图通过虚引用的get()
方法取得强引用时,总会失败。并且,虚引用必须和引用队列一起使用,它的作用在于跟踪垃圾回收过程。
当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象后,将这个虚引用加入引用队列,以通知应用程序对象的回收情况。
使用场景:Jvm
, Netty
(回收堆外内存)