文章

java重拾-四种引用

java重拾-四种引用

StrongReference

对对象直接的引用, 若一个对象存在任何Strong Reference, 则gc不会将其回收, 而是在内存不足时抛出OutOfMemoryError

@Test  
public void strongReference() {  
    Object referent = new Object();      
    /** 
     * 通过赋值创建 StrongReference  
     */  
    Object strongReference = referent;  
    
    assertSame(referent, strongReference);   
    referent = null;  
    System.gc();  
    
    /** 
     * StrongReference 在 GC 后不会被回收 
     */  
    assertNotNull(strongReference);  
}  

SoftReference

手动创建的SoftReference对象, 若一个对象只有SoftReference, 在内存充足时不会gc, 一旦内存不足就会gc

@Test  
public void softReference() {  
    Object referent = new Object();  
    SoftReference<Object> softRerference = new SoftReference<Object>(referent);  
  
    assertNotNull(softRerference.get());  
    
    referent = null;  
    System.gc();  
    
    /** 
     *  soft references 只有在 jvm OutOfMemory 之前才会被回收, 所以它非常适合缓存应用 
     */  
    assertNotNull(softRerference.get());  
}  

WeakReference

也是手动创建的, 若一个对象只有WeakReference时, 就会被销毁, WeakReference只承担一个标签之类的作用, 并尽量gc

@Test
    public void weakReference() {
        Object referent = new Object();

        WeakReference<Object> weakReference = new WeakReference<Object>(referent);
        assertSame(referent, weakReference.get());
        referent = null;
        System.gc();
        /**
         * 一旦没有指向 referent 的强/软引用, weak reference 在 GC 后会被自动回收
         */
        assertNull(weakReference.get());
    }

PhantomReference

虚引用(PhantomReference) 与 WeakReference 和 SoftReference 有很大的不同, 因为它的 get() 方法永远返回 null, 这也正是它名字的由来,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。
虚引用作用:用来跟踪对象被垃圾回收的活动。
虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列(ReferenceQueue)联合使用。
当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动

当弱引用的指向对象变得弱引用可到达,该弱引用就会加入到引用队列。这一操作发生在对象析构或者垃圾回收真正发生之前。理论上,这个即将被回收的对象是可以在一个不符合规范的析构方法里面重新复活。但是这个弱引用会销毁。虚引用只有在其指向的对象从内存中移除掉之前才会加入到引用队列中。其get方法一直返回null就是为了阻止其指向的几乎被销毁的对象重新复活。

虚引用使用场景主要由两个:

  • 它允许你知道具体何时其引用的对象从内存中移除。而实际上这是Java中唯一的方式。这一点尤其表现在处理类似图片的大文件的情况。当你确定一个图片数据对象应该被回收,你可以利用虚引用来判断这个对象回收之后在继续加载下一张图片。这样可以尽可能地避免可怕的内存溢出错误。
    虚引用可以避免很多析构时的问题。finalize方法可以通过创建强引用指向快被销毁的对象来让这些对象重新复活。
  • 然而,一个重写了 finalize方法的对象如果想要被回收掉,需要经历两个单独的垃圾收集周期。在第一个周期中,某个对象被标记为可回收,进而才能进行析构。但是因为在析构过程中仍有微弱的可能这个对象会重新复活。这种情况下,在这个对象真实销毁之前,垃圾回收器需要再次运行。因为析构可能并不是很及时,所以在调用对象的析构之前,需要经历数量不确定的垃圾收集周期。这就意味着在真正清理掉这个对象的时候可能发生很大的延迟。这就是为什么当大部分堆被标记成垃圾时还是会出现烦人的内存溢出错误。
    使用虚引用,上述情况将引刃而解,当一个虚引用加入到引用队列时,你绝对没有办法得到一个销毁了的对象。因为这时候,对象已经从内存中销毁了。因为虚引用不能被用作让其指向的对象重生,所以其对象会在垃圾回收的第一个周期就将被清理掉。
    显而易见,finalize方法不建议被重写。因为虚引用明显地安全高效,去掉finalize方法可以虚拟机变得明显简单
License:  CC BY 4.0