生活
任何傻瓜都能寫出計算機可以了解的代碼。好的程式員能寫出人能讀懂的代碼。
前言
在JAVA中,開發人員不需要像C開發人員那樣手動去管理記憶體中對象的生命周期,但是如果需要某些對象具備一定的生命周期(當記憶體不足的時候可以回收一些沒有必要的對象,進而規避一些OOM的風險),此時可以通過弱引用和軟引用、虛引用來實作生命周期的管理。
強引用
注意:JAVA中寫的大部分代碼都是強引用。
當一個對象被一個強引用關聯時,JVM必定不會回收這個對象,就算記憶體不足了,甯可出現OOM也不回收。
例如這段代碼:
public static void test() {
Object[] object = new Object[10000];
}
在配置設定記憶體給數組時,即使出現記憶體不足,也不會回收。
注意在這個方法執行完畢後,object這個強引用也就不存在了,那數組對象就能被jvm回收了。
軟引用
軟引用用來描述一些有用但又沒那麼必須的對象,,在Java中用java.lang.ref.SoftReference類來表示。被軟引用關聯的對象在記憶體不足時就會被JVM回收,這一點可以很好的用在oom的場景上,并且這個特性适合用在緩存上:比如網頁緩存、圖檔緩存。
軟引用可以與一個引用隊列關聯使用,當一個軟引用所引用的對象被JVM回收,這個軟引用就會被加入到與之關聯的引用隊列上。
代碼如下:
SoftReference<String> s = new SoftReference<String>(new String("a"));
System.out.println(s.get());
弱引用
弱引用用來描述一些非必須對象,它的優先級比軟引用還要低,在JAVA中用java.lang.ref.WeakRefrence來表示。
被弱引用關聯的對象,在JVM進行垃圾回收時,無論記憶體是否充足,都會回收。
同樣,弱引用也可以與一個引用隊列關聯使用,當一個弱引用所描述的對象被JVM回收,這個弱引用就會被加入與之關聯的引用隊列上。
ReferenceQueue<String> q = new ReferenceQueue<>();
WeakReference<String> s = new WeakReference<String>(new String("a"),q);
System.out.println(s.get());
System.out.println(q.poll());
System.gc();
System.out.println(s.get());
System.out.println(q.poll());
結果:
a
null
null
[email protected]
虛引用
虛引用跟前面的弱引用、軟引用不一樣,它不影響對象的生命周期,被他所表示的對象可以随時被垃圾回收器回收。
在java中用java.lang.ref.PhantomReference類表示一個虛引用。
虛引用必須與引用隊列關聯使用,當JVM準備回收一個對象時,如果發現它存在虛引用,會先把它放到它關聯的引用隊列,程式可以通過引用隊列裡是否有相關的虛引用,來判斷對象是否即将被回收。
如果發現某個虛引用出現在引用隊列裡,既可以在這個引用所引用的對象的記憶體空間被回收之前做出一些必要的操作。
ReferenceQueue<String> queue = new ReferenceQueue<String>();
PhantomReference<String> pr = new PhantomReference<String>(new String("hello"), queue);
System.out.println(pr.get());
null
注意虛引用的GET取到的對象隻有null。
虛引用的一個使用:DirectByteBuffer
如何利用軟引用和弱引用解決OOM問題
前面講了關于軟引用和弱引用相關的基礎知識,那麼到底如何利用它們來優化程式性能,進而避免OOM的問題呢?
下面舉個例子,假如有一個應用需要讀取大量的本地圖檔,如果每次讀取圖檔都從硬碟讀取,則會嚴重影響性能,但是如果全部加載到記憶體當中,又有可能造成記憶體溢出,此時使用軟引用可以解決這個問題。
設計思路是:用一個HashMap來儲存圖檔的路徑 和 相應圖檔對象關聯的軟引用之間的映射關系,在記憶體不足時,JVM會自動回收這些緩存圖檔對象所占用的空間,進而有效地避免了OOM的問題。在Android開發中對于大量圖檔下載下傳會經常用到。