1. 程式人生 > >Java物件構建快取記憶體器

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);  
    }  
}