本文轉自:http://zhangjunhd.blog.51cto.com/113473/53092 作者:ZJ 07-12-1
轉載請註明出處、作者信息和本聲明。否則將追究法律責任。
1.對象的強、軟、弱和虛引用
<span style="font-weight: normal;"><span style="font-size:18px;">ReferenceQueue queue = new ReferenceQueue ();
PhantomReference pr = new PhantomReference (object, queue);</span></span>
程序可以通過判斷引用隊列中是否已經加入了虛引用,來了解被引用的對象是否將要被垃圾回收。如果程序發現某個虛引用已經被加入到引用隊列,那麼就可以在所引用的對象的內存被回收之前採取必要的行動。2.對象可及性的判斷
3.使用軟引用構建敏感數據的緩存
3.1 爲什麼需要使用軟引用
3.2 如果使用軟引用
<span style="font-size:18px;font-weight: normal;">MyObject aRef = new MyObject();
SoftReference aSoftRef=new SoftReference(aRef);</span>
<span style="font-size:18px;font-weight: normal;">aRef = null;</span>
此後,這個MyObject對象成爲了軟可及對象。如果垃圾收集線程進行內存垃圾收集,並不會因爲有一個SoftReference對該對象的引用而始終保留該對象。Java虛擬機的垃圾收集線程對軟可及對象和其他一般Java對象進行了區別對待:軟可及對象的清理是由垃圾收集線程根據其特定算法按照內存需求決定的。也就是說,垃圾收集線程會在虛擬機拋出OutOfMemoryError之前回收軟可及對象,而且虛擬機會盡可能優先回收長時間閒置不用的軟可及對象,對那些剛剛構建的或剛剛使用過的“新”軟可反對象會被虛擬機儘可能保留。在回收這些對象之前,我們可以通過:<span style="font-size:18px;font-weight: normal;">MyObject anotherRef=(MyObject)aSoftRef.get();</span>
重新獲得對該實例的強引用。而回收之後,調用get()方法就只能得到null了。3.3 使用ReferenceQueue清除失去了軟引用對象的SoftReference
<span style="font-size:18px;font-weight: normal;">ReferenceQueue queue = new ReferenceQueue();
SoftReference ref=new SoftReference(aMyObject, queue);</span>
<span style="font-size:18px;font-weight: normal;">SoftReference ref = null;
while ((ref = (EmployeeRef) q.poll()) != null) {
// 清除ref
}</span>
3.4通過軟可及對象重獲方法實現Java對象的高速緩存
<span style="font-size:18px;font-weight: normal;">public class Employee {
private String id;// 僱員的標識號碼
private String name;// 僱員姓名
private String department;// 該僱員所在部門
private String Phone;// 該僱員聯繫電話
private int salary;// 該僱員薪資
private String origin;// 該僱員信息的來源
// 構造方法
public Employee(String id) {
this.id = id;
getDataFromlnfoCenter();
}
// 到數據庫中取得僱員信息
private void getDataFromlnfoCenter() {
// 和數據庫建立連接井查詢該僱員的信息,將查詢結果賦值
// 給name,department,plone,salary等變量
// 同時將origin賦值爲"From DataBase"
}
……</span>
這個Employee類的構造方法中我們可以預見,如果每次需要查詢一個僱員的信息。哪怕是幾秒中之前剛剛查詢過的,都要重新構建一個實例,這是需要消耗很多時間的。下面是一個對Employee對象進行緩存的緩存器的定義:<span style="font-size:18px;font-weight: normal;">import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.Hashtable;
public class EmployeeCache {
static private EmployeeCache cache;// 一個Cache實例
private Hashtable<String,EmployeeRef> employeeRefs;// 用於Chche內容的存儲
private ReferenceQueue<Employee> q;// 垃圾Reference的隊列
// 繼承SoftReference,使得每一個實例都具有可識別的標識。
// 並且該標識與其在HashMap內的key相同。
private class EmployeeRef extends SoftReference<Employee> {
private String _key = "";
public EmployeeRef(Employee em, ReferenceQueue<Employee> q) {
super(em, q);
_key = em.getID();
}
}
// 構建一個緩存器實例
private EmployeeCache() {
employeeRefs = new Hashtable<String,EmployeeRef>();
q = new ReferenceQueue<Employee>();
}
// 取得緩存器實例
public static EmployeeCache getInstance() {
if (cache == null) {
cache = new EmployeeCache();
}
return cache;
}
// 以軟引用的方式對一個Employee對象的實例進行引用並保存該引用
private void cacheEmployee(Employee em) {
cleanCache();// 清除垃圾引用
EmployeeRef ref = new EmployeeRef(em, q);
employeeRefs.put(em.getID(), ref);
}
// 依據所指定的ID號,重新獲取相應Employee對象的實例
public Employee getEmployee(String ID) {
Employee em = null;
// 緩存中是否有該Employee實例的軟引用,如果有,從軟引用中取得。
if (employeeRefs.containsKey(ID)) {
EmployeeRef ref = (EmployeeRef) employeeRefs.get(ID);
em = (Employee) ref.get();
}
// 如果沒有軟引用,或者從軟引用中得到的實例是null,重新構建一個實例,
// 並保存對這個新建實例的軟引用
if (em == null) {
em = new Employee(ID);
System.out.println("Retrieve From EmployeeInfoCenter. ID=" + ID);
this.cacheEmployee(em);
}
return em;
}
// 清除那些所軟引用的Employee對象已經被回收的EmployeeRef對象
private void cleanCache() {
EmployeeRef ref = null;
while ((ref = (EmployeeRef) q.poll()) != null) {
employeeRefs.remove(ref._key);
}
}
// 清除Cache內的全部內容
public void clearCache() {
cleanCache();
employeeRefs.clear();
System.gc();
System.runFinalization();
}
}</span>
4.使用弱引用構建非敏感數據的緩存
4.1全局 Map 造成的內存泄漏
<span style="font-size:18px;font-weight: normal;">public class SocketManager {
private Map<Socket, User> m = new HashMap<Socket, User>();
public void setUser(Socket s, User u) {
m.put(s, u);
}
public User getUser(Socket s) {
return m.get(s);
}
public void removeUser(Socket s) {
m.remove(s);
}
}</span>
4.2如何使用WeakHashMap
<span style="font-size:18px;font-weight: normal;">import java.util.WeakHashMap;
class Element {
private String ident;
public Element(String id) {
ident = id;
}
public String toString() {
return ident;
}
public int hashCode() {
return ident.hashCode();
}
public boolean equals(Object obj) {
return obj instanceof Element && ident.equals(((Element) obj).ident);
}
protected void finalize(){
System.out.println("Finalizing "+getClass().getSimpleName()+" "+ident);
}
}
class Key extends Element{
public Key(String id){
super(id);
}
}
class Value extends Element{
public Value (String id){
super(id);
}
}
public class CanonicalMapping {
public static void main(String[] args){
int size=1000;
Key[] keys=new Key[size];
WeakHashMap<Key,Value> map=new WeakHashMap<Key,Value>();
for(int i=0;i<size;i++){
Key k=new Key(Integer.toString(i));
Value v=new Value(Integer.toString(i));
if(i%3==0)
keys[i]=k;
map.put(k, v);
}
System.gc();
}
}</span>
4.3用 WeakHashMap 堵住泄漏
<span style="font-size:18px;font-weight: normal;">public class SocketManager {
private Map<Socket,User> m = new WeakHashMap<Socket,User>();
public void setUser(Socket s, User u) {
m.put(s, u);
}
public User getUser(Socket s) {
return m.get(s);
}
}</span>
4.4配合使用引用隊列
5.UML:使用關聯類指明特定形式的引用
也可以如下的構造型方式。
6.參考資料