本文概述
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.软引用
除FinalReference外, SoftReference是最强的引用类型。可以通过java.lang.ref.SoftReference使用SoftReferences。 JVM不会快速回收持有SoftReference的对象。 JVM将根据当前堆的使用情况确定何时回收。仅当堆使用率接近阈值时, 才会收集SoftReferenced对象。因此, SoftReference可用于实现对内存敏感的缓存。
SoftReference的特征是可以保留对Java对象的软引用的实例。软引用的存在不会阻止垃圾回收线程回收Java对象。这意味着一旦SoftReference保存了对Java对象的软引用, 在GC线程回收Java对象之前, SoftReference类提供的get()方法将返回对该Java对象的强引用。一旦GC线程回收了Java对象, get()方法将返回null。
以下是如何使用SoftReferences的示例。
在你的IDE中设置参数-Xmx2m -Xms2m, 并将堆内存大小指定为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是一种参考类型, 比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的对象几乎与没有引用相同, 并且可以随时由垃圾收集器回收。当尝试通过PhantomReferences的get()方法获得强大的引用时, 它将始终失败。并且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
PhantomReference上的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, 因为无法自动清除所有项目。
评论前必须登录!
注册