導讀
- 移動開發知識體系總章(Java基礎、Android、Flutter)
- 基本數據類型
- 引用類型
- 強引用(FinalReference)
- 軟引用(SoftReference)
- 弱引用(WeakReference)
- 虛引用(PhantomReference)
- 引用隊列(ReferenceQueue)
- Android中有哪些軟引用的使用場景?
引用類型
Java為每種基本類型都提供了對應的封裝類型,分別為:Byte、Short、Integer、Long、Float、Double、Character、Boolean。引用類型是一種對象類型,它的值是指向內存空間的引用
,就是地址。(操作內存中元素的方式)
在Java中提供了四個級別的引用:強引用、軟引用、弱引用、虛引用
。在這四個引用類型中,只有強引用java.lang.ref.FinalReference
類是包內可見,其他三種引用類型均為public,可以在應用程序中直接使用。引用類型的類結構如圖所示。
強引用(FinalReference)
Java中默認聲明的就是強引用,比如:
Object obj = new Object(); //只要obj還指向Object對象,Object對象就不會被回收
obj = null; //手動置null
只要強引用存在,垃圾回收器將永遠不會回收被引用的對象,哪怕內存不足時,JVM也會直接拋出OutOfMemoryError,也不會去回收強引用的對象。如果想中斷強引用與對象之間的聯系,可以顯示的將強引用賦值為null,這樣一來,JVM就可以適時的回收對象了。
強引用的特點:
- 強引用可以直接訪問目標對象。
- 強引用所指向的對象在任何時候都不會被系統回收。JVM寧愿拋出OOM異常,也不會回收強引用所指向的對象。
- 強引用可能導致內存泄漏。
軟引用(SoftReference)
軟引用是除了強引用
外,最強的引用類型。可以通過java.lang.ref.SoftReference
使用軟引用。在內存足夠的時候,軟引用對象不會被回收,只有在內存不足時,系統則會回收軟引用對象
,如果回收了軟引用對象之后仍然沒有足夠的內存,才會拋出內存溢出異常。這種特性常常被用來實現緩存技術,比如網頁緩存,圖片緩存等對內存敏感的高速緩存。
SoftReference的特點是它的一個實例保存對一個Java對象的軟引用, 該軟引用的存在不妨礙垃圾收集線程對該Java對象的回收。也就是說,一旦SoftReference保存了對一個Java對象的軟引用后,在垃圾線程對 這個Java對象回收前,SoftReference類所提供的get()方法返回Java對象的強引用。一旦垃圾線程回收該Java對象之后,get()方法將返回null。
下面舉一個例子說明軟引用的使用方法。
在IDE設置參數 -Xmx2m -Xms2m規定堆內存大小為2m。
@Test
public void test3(){
MyObject obj = new myObject();
SoftReference sf = new SoftReference<>(obj);
obj = null;
System.gc();
// byte[] bytes = new byte[1024*100];
// System.gc();
System.out.println("是否被回收"+sf.get());
}
運行結果:
是否被回收cn.zyzpp.MyObject@42110406
現在打開被注釋掉的new byte[1024*100]語句,這條語句請求一塊大的堆空間,使堆內存使用緊張。并顯式的再調用一次GC,結果如下:
是否被回收null
說明在系統內存緊張的情況下,軟引用被回收。
弱引用(WeakReference)
弱引用的引用強度比軟引用要更弱一些,無論內存是否足夠,只要 JVM 開始進行垃圾回收,那些被弱引用關聯的對象都會被回收。用java.lang.ref.WeakReference
實例來保存對一個Java對象的弱引用。
public void test3(){
MyObject obj = new MyObject();
WeakReference sf = new WeakReference(obj);
obj = null;
System.out.println("是否被回收"+sf.get());
System.gc();
System.out.println("是否被回收"+sf.get());
}
運行結果:
是否被回收cn.zyzpp.MyObject@42110406
是否被回收null
軟引用,弱引用都非常適合來保存那些可有可無的緩存數據,如果這么做,當系統內存不足時,這些緩存數據會被回收,不會導致內存溢出。而當內存資源充足時,這些緩存數據又可以存在相當長的時間,從而起到加速系統的作用。
虛引用(PhantomReference)
虛引用是最弱的一種引用關系,如果一個對象僅持有虛引用,那么它就和沒有任何引用一樣,它隨時可能會被回收,用java.lang.ref.PhantomReference
類來表示,通過查看這個類的源碼,發現它只有一個構造函數和一個 get() 方法,而且它的 get() 方法僅僅是返回一個null
,也就是說將永遠無法通過虛引用來獲取對象,虛引用必須要和 ReferenceQueue 引用隊列一起使用,它的作用在于跟蹤垃圾回收過程。
public class PhantomReference<T> extends Reference<T> {
/**
* Returns this reference object's referent. Because the referent of a
* phantom reference is always inaccessible, this method always returns
* <code>null</code>.
*
* @return <code>null</code>
*/
public T get() {
return null;
}
public PhantomReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
}
}
當垃圾回收器準備回收一個對象時,如果發現它還有虛引用,就會在垃圾回收后,銷毀這個對象,將這個虛引用加入引用隊列。程序可以通過判斷引用隊列中是否已經加入了虛引用,來了解被引用的對象是否將要被垃圾回收。如果程序發現某個虛引用已經被加入到引用隊列,那么就可以在所引用的對象的內存被回收之前采取必要的行動。
public void test3(){
MyObject obj = new MyObject();
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
PhantomReference sf = new PhantomReference<>(obj,referenceQueue);
obj = null;
System.out.println("是否被回收"+sf.get());
System.gc();
System.out.println("是否被回收"+sf.get());
}
運行結果:
是否被回收null
是否被回收null
引用隊列(ReferenceQueue)
引用隊列由ReferenceQueue類表示,它用于保存被回收后對象的引用。當聯合使用軟引用、弱引用和引用隊列時,系統在回收被引用的對象之后,將把它所回收對象對應的引用添加到關聯的引用隊列中。而虛引用在對象被釋放之前,將把它對應的虛引用添加到它關聯的引用隊列中,這樣就可以在對象被回收之前采取一些必要的措施。
ReferenceQueue queue = new ReferenceQueue();
SoftReference ref = new SoftReference(aMyObject, queue);
與軟引用、弱引用不同,虛引用必須和引用隊列一起使用
。
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
PhantomReference sf = new PhantomReference<>(obj,referenceQueue);
Android中有哪些軟引用的使用場景?
利用軟引用、弱引用的機制可以進行OOM等優化:
- 若項目用到大量的默認圖片,而這些圖片在很多地方會用到,如果每次都去讀取圖片,由于讀取文件需要硬件操作,速度較慢,會導致性能較低。所以我們考慮將圖片緩存起來,需要的時候直接從內存中讀取。但是,由于圖片占用內存空間比較大,緩存很多圖片需要很多的內存,就可能比較容易發生OOM異常。這時,我們可以考慮使用軟引用技術來避免這個問題發生。
//定義一個HashMap,保存軟引用對象。
private Map<String, SoftReference<Bitmap>> imageCache = new HashMap<String, SoftReference<Bitmap>>();
//定義一個方法,保存Bitmap的軟引用到HashMap。
public void addBitmapToCache(String path) {
// 強引用的Bitmap對象
Bitmap bitmap = BitmapFactory.decodeFile(path);
// 軟引用的Bitmap對象
SoftReference<Bitmap> softBitmap = new SoftReference<Bitmap>(bitmap);
// 添加該對象到Map中使其緩存
imageCache.put(path, softBitmap);
}
//獲取的時候,通過SoftReference的get()方法得到Bitmap對象。
public Bitmap getBitmapByPath(String path) {
// 從緩存中取軟引用的Bitmap對象
SoftReference<Bitmap> softBitmap = imageCache.get(path);
// 判斷是否存在軟引用
if (softBitmap == null) {
return null;
}
// 取出Bitmap對象,如果由于內存不足Bitmap被回收,將取得空
Bitmap bitmap = softBitmap.get();
return bitmap;
}
最后取出時,一定要進行非空判斷,Java代碼一定要堅持不信任原則,避免空指針。
- imageloder在處理緩存優化時也是用到了軟引用。
- 在Snackbar源碼中也使用了弱引用WeakReference,用來管理Snackbar。
- 在UniversalImageLoader 源碼中也使用了軟引用、弱引用