Reference 概念

来源:互联网 发布:新时代股票交易软件 编辑:程序博客网 时间:2024/04/30 06:30

转自:http://kenwublog.com/arrange-java-reference-concept

Strong Reference, 强引用,即java标准的引用方式,表示GC从 Root Set 开始向下扫描,可以找到对应的 Strong Reference。

Referent,被包装为 Weak, Soft, Phantom Reference的对象引用称之为 referent。后面的内容会多次提到这个名词。

Weak Reference, 弱引用。当一个referent,在运行时没有同时被强,软引用,只被Weak Reference自身引用,且Weak Reference从 Root Set 可达,则该referent会被GC回收。

WR的作用,一般是为referent提供一个被回收的凭据,结合ReferenceQueue可以让程序在第一时间得到referent被回收的事件,从而做一些额外的clean操作。(如果对ReferenceQueue作用和回调感兴趣,可以先看最下面的 ReferenceQueue 简介)

 

Soft Reference, 软引用。它是除strong外,生命周期最长的一种 Reference,只有当JVM Heap中充满Strong References, Full GC无法为heap腾出更多空间而即将抛出OOM时,SoftReferences会被GC回收。

SR的作用一般是用作不限大小的 cache(无需remove)。
比如将其 Soft Reference 无限地放入 hashmap 中,而不关心hashmap内的对象数量是否会撑爆heap,也不需要手动 remove。
当Heap容量达到OOM触发条件时,VM会自动回收该map里的所有SoftReferences.

Phanton Reference, 是一种特殊的Reference,正如他的名字所表达的,幻影引用,他可以像幻影一样附着在referent上。
当GC在遍历引用关系时,如果发现被phantom reference包装过的referent不存在strong, weak, soft引用时(就是除phantom外没有任何引用,幻影的由来),GC会将 phantom reference 放入 Reference queue。以便程序在另一边通过queue的remove/poll方法,感知referent被GC回收的事件。(如果对ReferenceQueue作用和回调感兴趣,可以先看最下面 ReferenceQueue 简介)

另外,我们知道,GC在回收对象前会先调用对象自身的finalize()方法,如果它有实现的话,然后再清掉内存。而Phantom Reference的回调(enqueue)是在对象的finalize后,回收前触发。这跟 WeakReference不一样。WR是在回收后才通知的。在这个特殊的阶段可以做一些特殊的clean操作。

为什么 Phantom Reference 的get总是返回null?
因为phantom reference想做到幻影(除自身外,不跟其他任何引用有关联),所以不允许程序能通过自身的get方法得到referent,而破坏幻影的初衷。

实例代码

package com.kenwublog.reference;
 
import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.util.HashMap;
 
public class PhantomReferenceTest {
 
 public static void main(String[] args) {
  ReferenceQueue referenceQueue = new ReferenceQueue();
  Object object = new Object() {
   public String toString() {
    return "Referenced Object";
   }
  };
 
  Object data = new Object() {
   public String toString() {
    return "Data";
   }
  };
 
  HashMap map = new HashMap();
  Reference reference = null;
  System.out.println("Testing PhantomReference.");
  reference = new PhantomReference(object, referenceQueue);
 
  map.put(reference, data);
 
  System.out.println(reference.get()); // null
  System.out.println(map.get(reference)); // Data
  System.out.println(reference.isEnqueued()); // false
 
  System.gc();
  System.out.println(reference.get()); // null
  System.out.println(map.get(reference)); // Data
  System.out.println(reference.isEnqueued()); // false
 
  object = null;
  data = null;
 
  System.gc();
  System.out.println(reference.get()); // null
  System.out.println(map.get(reference)); // Data
  System.out.println(reference.isEnqueued()); // true, because object has been reclaimed.
 }
 
}

ReferenceQueue,一种当 weak, soft, phantom的referent被GC回收后,提供事件回调的接口。需要在实例化三大reference时,通过构造函数传入,phantom reference是强制需要传入的,weak和soft可不传。

回调过程:

GC回收referent后(phantom是在回收前,finalize后),将reference enqueue到RQ中,程序通过调用RQ的remove方法来感知reference被GC回收的事件。
remove方法是阻塞的,当没有referent被回收时(GC未调用enqueue),remove方法会一直挂起线程,当有referent被回收时,该方法返回 referent对应的reference对象。
同样,RQ也提供了一个非阻塞的方法 poll,但这样就做不到实时回调了。

实例

package com.kenwublog.reference;
 
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.HashMap;
 
public class ReferenceQueueTest {
 
 public static void main(String[] args) {
  final ReferenceQueue q = new ReferenceQueue();
  String str = new String("AK47");
  WeakReference wr = new WeakReference(str, q);
 
  Thread t = new Thread(){
   @Override
   public void run() {
    try {
     Reference reference = q.remove();
     System.out.println(reference + " event fired.");
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
   }
  };
  t.setDaemon(true);
  t.start();
  System.out.println("Reference Queue is listening.");
 
  str = null; // clear strong reference
  System.out.println("Ready to gc");
  System.gc();
  try {
   Thread.sleep(2000);
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
  System.out.println("wr.get: " + wr.get());
 }
 
}

 

 

 

Java中的几种引用方式

    Java中有几种不同的引用方式,它们分别是:强引用、软引用、弱引用和虚引用。下面,我们首先详细地了解下这几种引用方式的意义。

    
      强引用

在此之前我们介绍的内容中所使用的引用都是强引用,这是使用最普遍的引用。如果一个对象具有强引用,那就类似于必不可少的生活用品,垃圾回收器绝不会回收它。当内存空 间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。

软引用(SoftReference

SoftReference 类的一个典型用途就是用于内存敏感的高速缓存。SoftReference 的原理是:在保持对对象的引用时保证在 JVM 报告内存不足情况之前将清除所有的软引用。关键之处在于,垃圾收集器在运行时可能会(也可能不会)释放软可及对象。对象是否被释放取决于垃圾收集器的算法 以及垃圾收集器运行时可用的内存数量。

弱引用(WeakReference

WeakReference 类的一个典型用途就是规范化映射(canonicalized mapping)。另外,对于那些生存期相对较长而且重新创建的开销也不高的对象来说,弱引用也比较有用。关键之处在于,垃圾收集器运行时如果碰到了弱可及对象,将释放 WeakReference 引用的对象。然而,请注意,垃圾收集器可能要运行多次才能找到并释放弱可及对象。

虚引用(PhantomReference

PhantomReference 类只能用于跟踪对被引用对象即将进行的收集。同样,它还能用于执行 pre-mortem 清除操作。PhantomReference 必须与 ReferenceQueue 类一起使用。需要 ReferenceQueue 是因为它能够充当通知机制。当垃圾收集器确定了某个对象是虚可及对象时,PhantomReference 对象就被放在它的 ReferenceQueue 上。将 PhantomReference 对象放在 ReferenceQueue 上也就是一个通知,表明 PhantomReference 对象引用的对象已经结束,可供收集了。这使您能够刚好在对象占用的内存被回收之前采取行动。ReferenceReferenceQueue的配合使用。

GCReferenceReferenceQueue的交互

A、 GC无法删除存在强引用的对象的内存。

B、 GC发现一个只有软引用的对象内存,那么:

① SoftReference对象的referent 域被设置为null,从而使该对象不再引用heap对象。

② SoftReference引用过的heap对象被声明为finalizable

③ 当 heap 对象的 finalize() 方法被运行而且该对象占用的内存被释放,SoftReference 对象就被添加到它的 ReferenceQueue(如果后者存在的话)。

C、 GC发现一个只有弱引用的对象内存,那么:

① WeakReference对象的referent域被设置为null,从而使该对象不再引用heap对象。

② WeakReference引用过的heap对象被声明为finalizable

③ heap对象的finalize()方法被运行而且该对象占用的内存被释放时,WeakReference对象就被添加到它的ReferenceQueue(如果后者存在的话)。

D、 GC发现一个只有虚引用的对象内存,那么:

① PhantomReference引用过的heap对象被声明为finalizable

② PhantomReference在堆对象被释放之前就被添加到它的ReferenceQueue

值得注意的地方有以下几点:

1GC在一般情况下不会发现软引用的内存对象,只有在内存明显不足的时候才会发现并释放软引用对象的内存。

2GC对弱引用的发现和释放也不是立即的,有时需要重复几次GC,才会发现并释放弱引用的内存对象。
3、软引用和弱引用在添加到ReferenceQueue的时候,其指向真实内存的引用已经被置为空了,相关的内存也已经被释放掉了。而虚引用在添加到ReferenceQueue的时候,内存还没有释放,仍然可以对其进行访问。

    代码示例

通过以上的介绍,相信您对Java的引用机制以及几种引用方式的异同已经有了一定了解。光是概念,可能过于抽象,下面我们通过一个例子来演示如何在代码中使用Reference机制。

 

1     String str = new String("hello"); //
2     ReferenceQueue<String> rq = new ReferenceQueue<String>(); //
3     WeakReference<String> wf = new WeakReference<String>(str, rq); //
4     str=null//④取消"hello"对象的强引用
5     String str1=wf.get(); //⑤假如"hello"对象没有被回收,str1引用"hello"对象
6     //假如"hello"对象没有被回收,rq.poll()返回null
7     Reference<? extends String> ref=rq.poll(); //

 

 

在以上代码中,注意⑤⑥两处地方。假如“hello”对象没有被回收wf.get()将返回“hello”字符串对象,rq.poll()返回null;而加入“hello”对象已经被回收了,那么wf.get()返回nullrq.poll()返回Reference对象,但是此Reference对象中已经没有str对象的引用了(PhantomReference则与WeakReferenceSoftReference不同)

    引用机制与复杂数据结构的联合应用

    了解了GC机制、引用机制,并配合上ReferenceQueue,我们就可以实现一些防止内存溢出的复杂数据类型。

例如,SoftReference具有构建Cache系统的特质,因此我们可以结合哈希表实现一个简单的缓存系统。这样既能保证能够尽可能多的缓存信息,又可以保证Java虚拟机不会因为内存泄露而抛出OutOfMemoryError。这种缓存机制特别适合于内存对象生命周期长,且生成内存对象的耗时比较长的情况,例如缓存列表封面图片等。对于一些生命周期较长,但是生成内存对象开销不大的情况,使用WeakReference能够达到更好的内存管理的效果。

SoftHashmap的源码一份,相信看过之后,大家会对Reference机制的应用有更深入的理解。

 

  1package com.***.widget;
  2
  3//: SoftHashMap.java 
  4import java.util.*
  5import java.lang.ref.*
  6
  7import android.util.Log;
  8
  9public class SoftHashMap extends AbstractMap 
 10  /** The internal HashMap that will hold the SoftReference. */ 
 11  private final Map hash = new HashMap(); 
 12  /** The number of "hard" references to hold internally. */ 
 13  private final int HARD_SIZE; 
 14  /** The FIFO list of hard references, order of last access. */ 
 15  private final LinkedList hardCache = new LinkedList(); 
 16  /** Reference queue for cleared SoftReference objects. */ 
 17  private ReferenceQueue queue = new ReferenceQueue(); 
 18
 19  //Strong Reference number
 20  public SoftHashMap() this(100); } 
 21  public SoftHashMap(int hardSize) { HARD_SIZE = hardSize; } 
 22  
 23
 24  public Object get(Object key) 
 25    Object result = null
 26    // We get the SoftReference represented by that key 
 27    SoftReference soft_ref = (SoftReference)hash.get(key); 
 28    if (soft_ref != null
 29      // From the SoftReference we get the value, which can be 
 30      // null if it was not in the map, or it was removed in 
 31      // the processQueue() method defined below 
 32      result = soft_ref.get(); 
 33      if (result == null
 34        // If the value has been garbage collected, remove the 
 35        // entry from the HashMap. 
 36        hash.remove(key); 
 37      }
 else 
 38        // We now add this object to the beginning of the hard 
 39        // reference queue.  One reference can occur more than 
 40        // once, because lookups of the FIFO queue are slow, so 
 41        // we don't want to search through it each time to remove 
 42        // duplicates. 
 43          //keep recent use object in memory
 44        hardCache.addFirst(result); 
 45        if (hardCache.size() > HARD_SIZE) 
 46          // Remove the last entry if list longer than HARD_SIZE 
 47          hardCache.removeLast(); 
 48        }
 
 49      }
 
 50    }
 
 51    return result; 
 52  }
 
 53
 54  /** We define our own subclass of SoftReference which contains 
 55   not only the value but also the key to make it easier to find 
 56   the entry in the HashMap after it's been garbage collected. */
 
 57  private static class SoftValue extends SoftReference 
 58    private final Object key; // always make data member final 
 59    /** Did you know that an outer class can access private data 
 60     members and methods of an inner class?  I didn't know that! 
 61     I thought it was only the inner class who could access the 
 62     outer class's private information.  An outer class can also 
 63     access private members of an inner class inside its inner 
 64     class. */
 
 65    private SoftValue(Object k, Object key, ReferenceQueue q) 
 66      super(k, q); 
 67      this.key = key; 
 68    }
 
 69  }
 
 70
 71  /** Here we go through the ReferenceQueue and remove garbage 
 72   collected SoftValue objects from the HashMap by looking them 
 73   up using the SoftValue.key data member. */
 
 74  public void processQueue() 
 75    SoftValue sv; 
 76    while ((sv = (SoftValue)queue.poll()) != null
 77        if(sv.get()== null){
 78            Log.e("processQueue""null");
 79        }
else{
 80            Log.e("processQueue""Not null");
 81        }

 82      hash.remove(sv.key); // we can access private data!
 83      Log.e("SoftHashMap""release " + sv.key);
 84    }
 
 85  }
 
 86  /** Here we put the key, value pair into the HashMap using 
 87   a SoftValue object. */
 
 88  public Object put(Object key, Object value) 
 89    processQueue(); // throw out garbage collected values first 
 90    Log.e("SoftHashMap""put into " + key);
 91    return hash.put(key, new SoftValue(value, key, queue)); 
 92  }
 
 93  public Object remove(Object key) 
 94    processQueue(); // throw out garbage collected values first 
 95    return hash.remove(key); 
 96  }
 
 97  public void clear() 
 98    hardCache.clear(); 
 99    processQueue(); // throw out garbage collected values 
100    hash.clear(); 
101  }
 
102  public int size() 
103    processQueue(); // throw out garbage collected values first 
104    return hash.size(); 
105  }
 
106  public Set entrySet() 
107    // no, no, you may NOT do that!!! GRRR 
108    throw new UnsupportedOperationException(); 
109  }
 
110}
 
111
112
113

 

 
 
 
0 0
原创粉丝点击