发布于 5年前

您真的了解Java中的4种引用类型吗?

Java中提供了四个级别的引用:SoftReference,FinalReference,WeakReference和PhantomReference。在四种引用类型中,只有FinalReference类在包中可见,其他三种引用类型都是公共的,可以直接在应用程序中使用。参考类图: java

1. FinalReference

Java中的引用类似于C中的指针。您可以通过引用对堆中的对象进行操作。这是一个例子:

StringBuffer stringBuffer = new StringBuffer("Helloword");

该变量 str 指向StringBuffer实例所在的堆空间,您可以使用它来操作该对象 strjava

以下是FinalReferences的功能:

  1. FinalReferences 提供对目标对象的直接访问。
  2. FinalReferences 指向的对象在任何时候都不会被系统回收。JVM用于抛出OOM异常,而不是回收FinalReference指向的对象。
  3. 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,因为所有项目都无法自动清除。

©2020 edoou.com   京ICP备16001874号-3