轉(zhuǎn)自:
Java/Android中的強(qiáng)引用彻秆、軟引用楔绞、弱引用结闸、虛引用
引用分為四個(gè),從高到低的級(jí)別以此為強(qiáng)引用-軟引用-弱引用-虛引用.
- 引用類型
類別 | 回收機(jī)制 | 用途 | 生存時(shí)間 |
---|---|---|---|
強(qiáng)引用 | 從不回收 | 對(duì)象狀態(tài) | JVM停止運(yùn)行時(shí) |
軟引用SoftReference | 內(nèi)存不足時(shí)進(jìn)行回收 | 緩存 | 內(nèi)存不足 |
弱引用WeakReference | 對(duì)象不被引用時(shí)回收 | 緩存 | GC運(yùn)行后 |
虛引用 | 對(duì)象被回收時(shí) | 管理控制精確內(nèi)存穩(wěn)定性 | unknown |
1.強(qiáng)引用:
Qiang qiang = new Qiang();
Niu niu = new Niu(qiang);
強(qiáng)引用例子,niu持有qiang的引用,當(dāng)qiang=null的時(shí)候,此時(shí)GC 應(yīng)該去回收Qiang,但是由于之前的實(shí)例,被niu持有,因此,并不能回收,導(dǎo)致內(nèi)存泄漏,典型的引用泄漏.
2.軟引用SoftReference
A a = new A();
SoftReference<A> aSoftRef = new SoftReference<>(a);
A a1 = aSoftRef.get();
一個(gè)對(duì)象只具有軟引用,則內(nèi)存空間足夠酒朵,垃圾回收器就不會(huì)回收它桦锄;如果內(nèi)存空間不足了,就會(huì)回收這些對(duì)象的內(nèi)存蔫耽。
只要垃圾回收器沒有回收它结耀,該對(duì)象就可以被程序使用。
軟引用可以和一個(gè)引用隊(duì)列(ReferenceQueue)聯(lián)合使用匙铡,如果軟引用所引用的對(duì)象被垃圾回收图甜,Java虛擬機(jī)就會(huì)把這個(gè)軟引用加入到與之關(guān)聯(lián)的引用隊(duì)列中。
A a = new A();
ReferenceQueue refQueue = new ReferenceQueue();
SoftReference<A> aSoftRef = new SoftReference<>(a, refQueue);
A a1 = aSoftRef.get();
3.Android中的示例:
- 案例1:
在平常的Android開發(fā)者,有很多的圖片要顯示,如果是網(wǎng)絡(luò)圖片,則需要通過網(wǎng)絡(luò)解析獲取,但是,每次都從網(wǎng)絡(luò)解析圖片,會(huì)影響體驗(yàn),于是我們將會(huì)將請(qǐng)求到的圖片保存到本地.
但是,這就產(chǎn)生了另一個(gè)問題:每次我們從本地獲取,相對(duì)與我們從網(wǎng)絡(luò)請(qǐng)求后,直接從內(nèi)存獲取,效率更低.于是我們就還要在內(nèi)存也緩存一份(層級(jí)著名的LRUCache 就是這3級(jí)緩存)
但是因?yàn)閳D片的數(shù)量多,消耗內(nèi)存過大,內(nèi)存緩存的過程需要大量的內(nèi)存,內(nèi)存不夠就會(huì)OOM,這時(shí),便可以采用軟引用的技術(shù)解決問題
private Map<String, SoftReference> softReferenceMap = new HashMap<>();
/**
*
* @param path
*/
public void addBitmap(String path) {
//強(qiáng)引用的bitmap對(duì)象
//或者從網(wǎng)絡(luò)請(qǐng)求圖片Bitmap
Bitmap bitmap = BitmapFactory.decodeFile(path);
//軟引用的Bitmap對(duì)象
SoftReference<Bitmap> softBitmap = new SoftReference<>(bitmap);
//添加軟引用的Bitmap到map中時(shí)期緩存
softReferenceMap.put(path, softBitmap);
}
public Bitmap getBitmap(String path) {
//從緩存中取軟引用的bitmap對(duì)象
SoftReference<Bitmap> softBitmap = softReferenceMap.get(path);
//判斷 軟引用的 對(duì)象 是否已經(jīng)被回收
if (null == softBitmap) {
return null;
//TODO: 進(jìn)行從SD卡讀取的操作
}
//取出Bitmap對(duì)象,如果內(nèi)存不足,軟引用對(duì)象被回收了,將取到 null
Bitmap bitmap = softBitmap.get();
return bitmap;
}
在softBitmap.get()中獲取Bitmap的實(shí)例的強(qiáng)引用,在內(nèi)存充足的情況下不會(huì)回收軟引用對(duì)象,可以取出bitmap
內(nèi)存不足時(shí),softBitmap.get()不在返回bitamp直接返回null,軟引用被回收了
因此在獲取Bitmap的對(duì)象之前要判斷softBitmap == null是否為空,否則將會(huì)出現(xiàn)空指針異常.
這里,還可以擴(kuò)展從SD卡讀取緩存的操作
- 案例2:
設(shè)計(jì)給出了一張1080的全屏圖片,這張圖片假設(shè)500K,可以想象它的內(nèi)存消耗,通用使用軟引用來解決
private void test() {
ImageView imageView = findViewById(R.id.test_iv);
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_4444;
//5.0后失效 設(shè)置了也沒有
options.inPurgeable=true;
options.outWidth=720;
options.outHeight = 1280;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),
R.mipmap.avatar,
options);
Drawable drawable = new BitmapDrawable(getResources(), bitmap);
SoftReference<Drawable> drawableSoftReference = new SoftReference<Drawable>(drawable);
//判斷 是否 null 如果內(nèi)存不足,軟引用 被回收,那么就不展示在ImageView上
if (drawableSoftReference != null) {
imageView.setBackground(drawableSoftReference.get());
}
}
因此在特定的場景想要避免OOM的發(fā)生就盡量使用軟引用吧.
但是在Android中最好選擇Least Recently Used(LRU),在它內(nèi)部維護(hù)一個(gè)特定大小內(nèi)存,在內(nèi)存不足時(shí)會(huì)根據(jù)一系列的策略算法來進(jìn)行處理移除掉當(dāng)前一些緩存以便獲取新的內(nèi)空間用來緩存數(shù)據(jù).
4.弱引用WeakReference
WeakReference<MainActivity> weakReference = new WeakReference<MainActivity>(new MainActivity()) ;
如果一個(gè)對(duì)象只具有弱引用,那么在垃圾回收線程掃描的過程中,一旦發(fā)現(xiàn)了,只有弱引用的對(duì)象,不管當(dāng)前內(nèi)存空間是否足夠,都會(huì)回收他的內(nèi)存.
不過,由于垃圾回收器是一個(gè)優(yōu)先級(jí)很低的線程,因此不一定會(huì)很快發(fā)現(xiàn)哪些只具有弱引用的對(duì)象.
弱引用可以喝一個(gè)引用對(duì)列(ReferenceQueue)配合使用,如果弱引用所引用的對(duì)象被垃圾回收,java虛擬機(jī)就會(huì)把這個(gè)弱引用加入到與之關(guān)聯(lián)的引用對(duì)列之中.
5.Android中的示例
比如一個(gè)Activity持有一個(gè)Handler,handler是匿名內(nèi)部類的形式,而非,靜態(tài)內(nèi)部類,那么handler就會(huì)持有此activity的引用,Handler作為一個(gè)耗時(shí)的異步線程的通信工具,如果在異步線程處理任務(wù)過程中,Activity關(guān)閉了,此時(shí)Activity應(yīng)該被回收,但是因?yàn)镠andler還持有Activity的引用,而了另一個(gè)"異步線程"持有handler的引用,那么,就將導(dǎo)致內(nèi)存泄漏
- 解決方案:
- 在Activity關(guān)閉的地方,停止線程,并把handler的消息隊(duì)列的所有消息對(duì)象移除
- Handler改為靜態(tài)類
public class TestActivity extends AppCompatActivity {
private MyHandler mHandler = new MyHandler(this);
private static class MyHandler extends Handler {
private WeakReference<Context> ctxRef;
public MyHandler(Context context) {
ctxRef = new WeakReference<>(context);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (ctxRef.get() instanceof TestActivity) {
((TestActivity) ctxRef.get()).textView.setText("");
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
}
}
6.虛引用:
在對(duì)象銷毀時(shí)會(huì)被回收.
在Java中GC的運(yùn)行時(shí)間是不確定的,在Java里有一個(gè)finalize方法,在垃圾回收器準(zhǔn)備釋放內(nèi)存的時(shí)候鳖眼,會(huì)先調(diào)用finalize().但因?yàn)閮?nèi)存還沒有好耗盡,擬機(jī)不能保證在適當(dāng)?shù)臅r(shí)機(jī)調(diào)用finalize,因此垃圾回收期與finalize是不可靠的方法.
這時(shí)可以采用虛引用.比如一個(gè)固定的內(nèi)存,在明確知道一個(gè)bitmap回收之后會(huì)釋放一部分內(nèi)存,新釋放開辟的內(nèi)存就可以讓其他bitmap來使用,以此循環(huán)來達(dá)到內(nèi)存的穩(wěn)定性控制.
7.總結(jié)
在Android中比較常用便是 弱引用和軟引用了黑毅。
對(duì)于一些OOM等常規(guī)處理使用軟引用便可很好的解決,可以實(shí)現(xiàn)高速緩存.
對(duì)于偶爾使用的對(duì)象,并且隨時(shí)獲取到便使用弱引用來標(biāo)記
軟引用與弱引用的區(qū)別:只含有弱引用的對(duì)象的生命周期更短。在垃圾回收器線程掃描它所管轄的內(nèi)存區(qū)域的過程中钦讳,一旦發(fā)現(xiàn)了只具有弱引用的對(duì)象矿瘦,不管當(dāng)前內(nèi)存空間足夠與否,都會(huì)回收它的內(nèi)存愿卒。不過缚去,由于垃圾回收器是一個(gè)優(yōu)先級(jí)很低的線程,因此不一定會(huì)很快發(fā)現(xiàn)那些只具有弱引用的對(duì)象掘猿。
虛引用只要用于內(nèi)存的精準(zhǔn)控制,如Android中的ViewPager圖片的查看等等