忽然出現的Out Of Memory這個BUG致使咱們項目中斷了好幾天,在通過不斷地摸索以後,今天終於獲得瞭解決。鑑於其強大的破壞力與多發性(尤爲是當開發圖形豐富的軟件 時),在此將解決方法同你們分享,但願你們之後少走彎路,而本人水平有限,若有不當,還望指教!測試
那麼,首先讓咱們來看看遇到這個BUG時系統輸出的Log:spa
按咱們的經驗一行一行地分析,發現了報錯的緣由:bitmap size exceeds VM budget,設計
中文意思是bitmap佔用的內存大小超過了虛擬機(DVM)的容許值。code
帶着這個信息,我去問谷哥和度娘,果真有大把大把的人遇到了這個問題,有的人還長久以來身陷其中,難以自拔~~對象
而解決方案則是五花八門,可是有的網友卻反映這些網上通用的解決方案徹底沒做用!?圖片
我並無嘗試全部網上的解決方法,在嘗試了部分以後確實沒有起到多少做用,該出BUG的地方照出不誤,內存
搞得我甚至有點懷疑這是Google的一個設計缺陷。開發
通過信息檢索,我弄清了這樣一個事實:Android虛擬機不容許單個程序中的Bitmap佔用超過8M的內存,一旦超過了就會報錯,get
而報的錯正是bitmap size exceeds VM budget.虛擬機
如今好了,這一切看似如此簡單:要想程序的bitmap小於8M,要麼就在用了bitmap後當即回收這部份內存,要麼就壓縮圖片的大小啊。
依據這兩點思路,我在個人項目中進行了實踐。
(通常而言,只用這兩種方法就能夠解決大部分Out Of Memory的BUG,若是還不能解決,請繼續往下看)
第一種方法--及時回收bitmap內存:
通常而言,回收bitmap內存能夠用到如下代碼
if(bitmap != null && !bitmap.isRecycled()){
bitmap.recycle();
bitmap = null;
}
System.gc();
bitmap.recycle()方法用於回收該bitmap所佔用的內存,接着將bitmap置空,最後,別忘了用System.gc()調用一下系統的垃圾回收器。
在這裏要聲明一下,bitmap能夠有多個(覺得着能夠有多個if語句),但System.gc()最好只有一個(因此我將它寫在了if語句外),由於System.gc()
每次調用都要將整個內存掃描一遍,於是若是屢次調用的話會影響程序運行的速度。爲了程序的效率,我將它放在了全部回收語句以後,
這樣已經起到了它的效果,還節約的時間。
回收bitmap已經知道了,那麼「及時」怎麼理解呢?
根據個人實際經驗,bitmap發揮做用的地方要麼在View裏,要麼在Activity裏(固然確定有其餘區域,可是原理都是相似的),
回收bitmap的地方最好寫在這些區域剛剛不使用bitmap了的時刻。
好比說View若是使用了bitmap,就應該在這個View再也不繪製了的時候回收,或者是在跳轉到的下一個區域的代碼中回收;
再好比說SurfaceView,就應該在onSurfaceDestroyed這個方法中回收;
同理,若是Activity使用了bitmap,就能夠在onStop或者onDestroy方法中回收......
結合以上的共同點,「及時回收」的原理就是在使用了bitmap的區域結束時或結束後回收。
第二種方法--壓縮圖片:
這個方法固然很簡單了,就是使圖片體積大小變小,
能夠有兩種方式:
一種是使圖片質量下降(分辨率不變),
另外一種是使圖片分辨率下降(分辨率改變)。
總之,使圖片大小變小就好了。
實踐證實,使圖片質量下降(分辨率不變)能夠大幅度地減少體積,並且質量的差別肉眼看上去並不明顯。
我剛開始使用的就是這兩種方法,原理很簡單,但是,個人BUG發生雖然沒那麼頻繁了,可是它依然存在!!
後來通過幾天的努力與嘗試,結合我項目的一些具體狀況,我終於解決了這個使人頭痛的BUG,可是事實卻有點出乎個人意料。
當我使用了上述兩種方法BUG依然還沒解決的時候,我開始懷疑,bitmap超過8M會報錯,可如今我把前先後後的bitmap都回收了,
不可能還有8M了,那爲何還會報錯呢?
終於我發現了這個緣由:當內存中已經被一些bitmap使用過以後,不管被回收與否,它都會變得特別「敏感」,這個時候,
若是bitmap忽然要佔用大量的內存,即便和以前已經剩下的內存加起來不到8M,系統也會報錯,緣由是它變「敏感」了!
我不知道這個用底層原理如何解釋比較好,可是我想「敏感」這個詞應該能夠很形象地進行解釋。
因而,爲了順應內存的「敏感性」,我將那個須要同時裝載多個大致積bitmap的地方進行了修改,用到了如下方法:
//壓縮,用於節省BITMAP內存空間--解決BUG的關鍵步驟
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inSampleSize = 2; //這個的值壓縮的倍數(2的整數倍),數值越小,壓縮率越小,圖片越清晰
//返回原圖解碼以後的bitmap對象
bitmap = BitmapFactory.decodeResource(Context, ResourcesId, opts);
即先將圖片縮小一倍,再將這縮小了一倍的圖片做爲bitmap存入內存,這樣一來,它佔用的bitmap內存大大減少。
後來經測試,BUG果真解決了。圖片縮小一倍後,順應了內存的「敏感性」,也就不會再報錯了。
以上方法應該足以解決大多數bitmap內存溢出問題,可是具體狀況仍是要具體分析。