您真的了解Java中的4种引用类型吗?
Java中提供了四个级别的引用:SoftReference,FinalReference,WeakReference和PhantomReference。在四种引用类型中,只有FinalReference类在包中可见,其他三种引用类型都是公共的,可以直接在应用程序中使用。参考类图:
1. FinalReference
Java中的引用类似于C中的指针。您可以通过引用对堆中的对象进行操作。这是一个例子:
StringBuffer stringBuffer = new StringBuffer("Helloword");
该变量 str
指向StringBuffer实例所在的堆空间,您可以使用它来操作该对象 str
。
以下是FinalReferences的功能:
- FinalReferences 提供对目标对象的直接访问。
- FinalReferences 指向的对象在任何时候都不会被系统回收。JVM用于抛出OOM异常,而不是回收FinalReference指向的对象。
- FinalReferences 可能会导致内存泄漏。
2. SoftReference
SoftReference是除FinalReference之外最强的引用类型。SoftReferences可以通过使用java.lang.ref.SoftReference
。JVM不会快速回收持有SoftReference的对象。JVM将根据当前堆使用情况确定何时回收。仅当堆使用率接近阈值时,才会收集SoftReferenced对象。因此,SoftReference可用于实现对内存敏感的缓存。
SoftReference的特征在于它可以保存对Java对象的软引用的实例。软引用的存在不会阻止垃圾收集线程回收Java对象。这意味着一旦SoftReference保存了对Java对象的软引用,get()
SoftReference类提供的方法将在GC线程回收Java对象之前返回对Java对象的强引用。GC线程回收Java对象后,该get()
方法将返回null。
以下是如何使用SoftReferences的示例。
-Xmx2m -Xms2m
在IDE中设置参数,并将堆内存大小指定为2m。
@Test
public void test3(){
MyObject obj = new myObject();
SoftReference sf = new SoftReference<>(obj);
obj = null;
System.gc();
// byte[] bytes = new byte[1024*100];
// System.gc();
System.out.println("Is Collected: "+sf.get());
}
结果如下:
Is Collected: cn.zyzpp.MyObject@42110406
打开注释语句new byte [1024 * 100],它请求大块的堆空间,使堆内存紧张。再次显式调用GC,结果如下:
Is Collected: null
请注意,在系统内存不足的情况下,SoftReferences将被回收。
3. WeakReference
WeakReference是一种比SoftReference弱的引用。在GC系统中,只要找到弱引用,无论系统堆空间是否足够,对象都将被回收。您可以使用该java.lang.ref.WeakReference
实例来保存对Java对象的弱引用。
public void test3(){
MyObject obj = new MyObject();
WeakReference sf = new WeakReference(obj);
obj = null;
System.out.println("Is Collected: "+sf.get());
System.gc();
System.out.println("Is Collected: "+sf.get());
}
结果如下:
Is Collected: cn.zyzpp.MyObject@42110406
Is Collected: null
SoftReference和WeakReference非常适合用于保存可选的缓存数据。如果使用它们,当系统内存不足时,将回收缓存的数据而不会导致内存溢出。当内存资源充足时,缓存的数据可以存在很长时间,这将加速系统。
4. PhantomReference
PhantomReference是所有类型中最弱的。持有PhantomReference的对象几乎与没有引用相同,并且可以随时由垃圾收集器回收。当试图通过get()
PhantomReferences方法获得强引用时,它总是会失败。并且PhantomReferences必须与引用队列一起使用以跟踪垃圾收集过程。
当垃圾收集器要回收一个对象时,如果它发现有一个PhantomReference,它将在垃圾收集后销毁该对象并将PhantomReference添加到引用队列。根据判断是否已将PhantomReference添加到引用队列,程序可以确定是否需要对引用的对象进行垃圾回收。如果程序发现已将PhantomReference添加到引用队列,则将在引用对象的内存被回收之前执行必要的操作。
public void test3(){
MyObject obj = new MyObject();
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
PhantomReference sf = new PhantomReference<>(obj,referenceQueue);
obj = null;
System.out.println("Is Collected: "+sf.get());
System.gc();
System.out.println("Is Collected: "+sf.get());
}
结果如下:
Is Collected: null
Is Collected: null
在get()
对幻影操作始终返回null,因为实施sf.get()
方法如下:
public T get() {
return null;
}
5. WeakHashMap类及其实现
WeakHashMap类在java.util
包中。它实现了Map接口,是HashMap的一个实现。它使用WeakReferences作为内部数据的存储方案。WeakHashMap是弱引用的典型应用程序,可用作简单的缓存表解决方案。
以下两段代码分别使用WeakHashMap和HashMap来保存大量数据:
@Test
public void test4(){
Map map;
map = new WeakHashMap<String,Object>();
for (int i =0;i<10000;i++){
map.put("key"+i,new byte[i]);
}
// map = new HashMap<String,Object>();
// for (int i =0;i<10000;i++){
// map.put("key"+i,new byte[i]);
// }
}
它用于-Xmx2M
限制堆内存。使用WeakHashMap的代码结束运行,使用HashMap的代码段抛出异常。
java.lang.OutOfMemoryError: Java heap space
可以看出,当系统中存在内存压力时,WeakHashMap将使用WeakReferences,并自动释放持有WeakReferences的内存数据。
但是,如果WeakHashMap的键都在系统中保存FinalReferences,WeakHashMap将退化为普通的HashMap,因为所有项目都无法自动清除。