Java中引用分为强引用、软引用、弱引用、虚引用,本文探究一下各种引用的使用场景。
强引用Java中一般没有特别指明的都是强引用,最显著的标志是"=",“new”。
// 强引用
Object object = new Object();
若引用是强引用的话,那么该实例不会被垃圾回收器回收,即使内存不够用了,抛出OOM,也不会被回收。若是需要回收,需要移除引用关系。
// 移除引用关系,可被回收
object = null;
通过将实例置为null,表示该实例不再被引用,可以被垃圾回收器回收。好的开发习惯是有始有终,有开始就应该有结束,特别是大对象,使用完后及时置为null,或及时释放。
测试:
public static void main(String[] args) {
// 创建对象,强引用
SoftReferenceDemo.Person jerry = new SoftReferenceDemo.Person(18, "Jerry");
System.out.println("GC前");
// 输出引用数据
System.out.println(jerry);
// 垃圾回收
System.gc();
System.out.println("GC后");
// 输出回收之后引用数据
System.out.println(jerry);
// 占用内存1000m,大于10个G,出现OOM
linkedList memoryUse = new linkedList<>();
try {
for (int i = 0; i < 10000; i++) {
memoryUse.add(new byte[1024*1024*1]);
}
} catch (Throwable e) {
e.printStackTrace();
System.out.println("OOM后");
// 再次输出OOM回收之后引用数据
System.out.println(jerry);
}
}
结果:
GC前
Person{age=18, name='Jerry'}
GC后
Person{age=18, name='Jerry'}
java.lang.OutOfMemoryError: Java heap space
at java.base/java.util.linkedList.linkLast(linkedList.java:146)
at java.base/java.util.linkedList.add(linkedList.java:342)
at references.StrongReferenceDemo.main(StrongReferenceDemo.java:29)
OOM后
Person{age=18, name='Jerry'}
结果发现,GC和OOM后,强引用关联的对象依然还在。
软引用软引用也就是代码中的SoftReference。
public class SoftReferenceextends Reference { ... }
软引用用于引用一些有用但是非必需的对象,GC的时候可能会被回收,若内存够用则不管,若内存紧张,软引用关联的对象会被回收,若内存还是不够用,则出现OOM。软引用常用于图片缓存。
测试:
public static void main(String[] args) {
// 创建对象,强引用
Person jerry = new Person(18, "Jerry");
// 软引用
SoftReference personSoftReference = new SoftReference<>(jerry);
// 去掉强引用
jerry = null;
System.out.println("GC前");
// 输出软引用数据
System.out.println(personSoftReference.get());
// 垃圾回收
System.gc();
System.out.println("GC后");
// 输出回收之后软引用数据
System.out.println(personSoftReference.get());
// 占用内存10000m,大约10个G,定会出现OOM
linkedList memoryUse = new linkedList<>();
try {
for (int i = 0; i < 10000; i++) {
memoryUse.add(new byte[1024*1024*1]);
}
} catch (Throwable e) {
e.printStackTrace();
System.out.println("OOM后");
// 再次输出OOM回收之后软引用数据
System.out.println(personSoftReference.get());
}
}
public static class Person {
public int age;
public String name;
public Person(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + ''' +
'}';
}
}
结果:
GC前
Person{age=18, name='Jerry'}
GC后
Person{age=18, name='Jerry'}
java.lang.OutOfMemoryError: Java heap space
at java.base/java.util.linkedList.linkLast(linkedList.java:146)
at java.base/java.util.linkedList.add(linkedList.java:342)
at references.SoftReferenceDemo.main(SoftReferenceDemo.java:28)
OOM后
null
查看结果发现:在堆内存够用的情况下,GC后,软引用关联对象依然存在,说明此时软引用关联的对象没有被回收;当堆内存溢出出现OOM时,输出软引用关联对象为null,说明此时软引用关联的对象被回收,因为超出内存太多,所以简单回收软引用关联的对象已无法阻止OOM。
弱引用弱引用代码中对应的是WeakReference。
public class WeakReferenceextends Reference { ... }
弱引用,只要发生GC,弱引用关联的对象一定会被回收。
测试:
public static void main(String[] args) {
// 创建对象,强引用
SoftReferenceDemo.Person tom = new SoftReferenceDemo.Person(20, "Tom");
// 弱引用
WeakReference personWeakReference = new WeakReference<>(tom);
// 去掉强引用
tom = null;
System.out.println("GC前");
// 输出弱引用数据
System.out.println(personWeakReference.get());
// 垃圾回收
System.gc();
System.out.println("GC后");
// 输出回收之后弱引用数据
System.out.println(personWeakReference.get());
}
结果:
GC前
Person{age=20, name='Tom'}
GC后
null
GC后出现null,说明弱引用关联的对象只要发生GC就一定会被回收,无须出现OOM。软引用在Thread、ThreadLocal、HttpClientImpl等里面广泛使用,使用场景要比SoftReference广泛的的多。
虚引用虚引用在代码中对应的是PhantomReference。
public class PhantomReferenceextends Reference { ... }
虚引用随时可能被回收,用了感觉和没用一样。
测试:
public static void main(String[] args) {
// 创建对象,强引用
SoftReferenceDemo.Person tom = new SoftReferenceDemo.Person(20, "Tom");
// 引用队列
ReferenceQueue personReferenceQueue = new ReferenceQueue<>();
// 虚引用
PhantomReference personWeakReference = new PhantomReference<>(tom,personReferenceQueue);
// 去掉强引用
tom = null;
// 输出虚引用数据
System.out.println(personWeakReference.get());
}
结果:
null
虚引用需要搭配ReferenceQueue使用,结果输出为null,说明虚引用和没有引用一样,所以无法通过get获取对象实例。
虚引用使用场景较少,一般JVM内部使用,可用于做一些事后清理任务。为一个对象设置虚引用目的是为了能在这个对象被回收时收到系统通知。



