Java物件構建快取記憶體器
Java物件引用+ReferenceQueue實現Java物件的快取記憶體
Java物件的強、軟、弱和虛引用(使程式能更加靈活地控制物件的生命週期):
1.強引用(StrongReference):最普遍的引用,物件具有強引用,記憶體空間不足,JVM寧願丟擲OutOfMemoryError錯誤,GC絕不會回收它。
2.軟引用(SoftReference):如果物件只具有軟引用,記憶體空間足夠,GC不會回收;記憶體空間不足,就會回收這些物件的記憶體。軟引用可實現記憶體敏感的快取記憶體。
3.弱引用(WeakReference:相對於軟引用的區別是就算記憶體空間足夠,GC遇見弱引用物件也會回收。由於GC是一個較低優先順序的執行緒,不會很快發現弱引用。
弱引用與軟引用可以和一個引用佇列(ReferenceQueue)聯合使用,如果該引用所引用的物件被GC回收,JVM會把這個引用加入到與之關聯的引用佇列。
4.虛引用(PhantomReference):用來跟蹤物件被GC回收前的活動,虛引用必須和引用佇列 (ReferenceQueue)聯合使用。當垃圾回收器準備回收一個物件時,如果發現它有虛引用,就會在回收物件的記憶體之前,把這個虛引用加入到與之關聯的引用佇列中。Java物件可及性的判斷:
◆單條引用路徑可及性判斷:在這條路徑中,最弱的一個引用決定物件的可及性
◆多條引用路徑可及性判斷:幾條路徑中,最強的一條的引用決定物件的可及性使用軟引用構建敏感資料的快取(關鍵在於JVM回收軟可及物件是在虛擬機器丟擲OutOfMemoryError之前):
SoftReference特點是它的一個例項儲存對一個Java物件的軟引用,該軟引用的存在不妨礙垃圾收集執行緒對該Java物件的回收。也就是說,一旦SoftReference儲存了對一個Java物件的軟引用後,在垃圾執行緒對這個Java物件回收前,SoftReference類所提供的get()方法返回Java物件的強引用。另外,一旦垃圾執行緒回收該Java物件之後,get()方法將返回null。
MyObject aRef = new MyObject();
SoftReference aSoftRef=new SoftReference(aRef);
當我們結束aReference對這個MyObject例項的強引用:aRef = null之後,這個MyObject物件成為軟可及物件,JVM回收軟可及物件是在虛擬機器丟擲OutOfMemoryError之前,而且優先回收長時間不用的軟可及物件,剛剛使用過的“新”軟可反物件會被虛擬機器儘可能保留。回收這些物件之前,我們可以通過: MyObject anotherRef=(MyObject)aSoftRef.get(); 重新獲得對該例項的強引用。使用ReferenceQueue清除失去了軟引用物件的SoftReference:
1.SoftReference物件除了具有儲存軟引用的特殊性之外,也具有Java物件的一般性。
2.當軟可及物件被回收之後,雖然這個SoftReference物件的get()方法返回null,但這個SoftReference物件已經不再具有存在的價值,需要一個適當的清除機制,避免大量SoftReference物件帶來的記憶體洩漏。
3.在java.lang.ref包裡還提供了ReferenceQueue。如果在建立SoftReference物件的時候,使用了一個ReferenceQueue物件作為引數提供給SoftReference的構造方法。
4.在任何時候,我們都可以呼叫ReferenceQueue的poll()方法來檢查是否有它所關心的非強可及物件被回收。如果佇列為空,將返回一個null,否則該方法返回佇列中前面的一個Reference物件。
5.利用這個方法,我們可以檢查哪個SoftReference所軟引用的物件已經被回收。於是我們可以把這些失去所軟引用的物件的SoftReference物件清除掉。
ReferenceQueue queue = new ReferenceQueue();
SoftReference EmployeeRef=new SoftReference(aMyObject, queue);
SoftReference ref = null;
while ((ref = (EmployeeRef) q.poll()) != null) {
// 清除ref
}
使用弱引用構建非敏感資料的快取(全域性Map造成的記憶體洩漏,用WeakHashMap修復SocketManager。):
無意識物件保留最常見的原因是使用Map將元資料與臨時物件(transient object)相關聯。假定一個物件具有中等生命週期,比分配它的那個方法呼叫的生命週期長,但是比應用程式的生命週期短,如客戶機的套接字連線。需要將一些元資料與這個套接字關聯,如生成連線的使用者的標識。在建立Socket時是不知道這些資訊的,並且不能將資料新增到Socket物件上,因為不能控制Socket類或者它的子類。這時,典型的方法就是在一個全域性Map中儲存這些資訊,如下面的SocketManager類所示:使用一個全域性Map將元資料關聯到一個物件。在 SocketManager 中防止洩漏很容易,只要用 WeakHashMap 代替 HashMap 就行了。(這裡假定SocketManager不需要執行緒安全)。當對映的生命週期必須與鍵的生命週期聯絡在一起時,可以使用這種方法。
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);
}
}