android Memory Management, OutOfMemoryError Note
1
Android框架強(qiáng)制每個(gè)進(jìn)程的24 MB內(nèi)存限制。在一些舊的設(shè)備,如在G1,限制為16 MB 更低,更重要的是,由位圖使用的內(nèi)存限制。處理圖像的應(yīng)用程序,它是很容易達(dá)到此限制,并獲得與OOM 異常死亡 的過程:E / dalvikvm堆(12517):1048576字節(jié)外部分配這個(gè) 過程中過大的E / GraphicsJNI(12517): VM將不會(huì)讓我們分配1048576字節(jié) / AndroidRuntime(12517):關(guān)閉VM / dalvikvm(12517):主題ID = 1:線程未捕獲的異常退出(集團(tuán)= 0x4001d7f0 ) E / AndroidRuntime(12517):致命異常:主要 電子/ AndroidRuntime(12517):java.lang.OutOfMemoryError:位圖的大小超過VM的預(yù)算 ,這個(gè)限制是低得離譜 。設(shè)備,像512MB的物理RAM的Nexus之一,設(shè)置每個(gè)進(jìn)程的前臺(tái)活動(dòng)只有5%的RAM的內(nèi)存限制是一個(gè)愚蠢的錯(cuò)誤 。但無論如何,事情是如何和我們生活-即找到如何解決它。 遠(yuǎn)遠(yuǎn)超過限制的內(nèi)存分配方式有兩種 : 一種方法是從本機(jī)代碼分配內(nèi)存 。使用NDK(本地開發(fā)工具包)和JNI,它可能從C級(如的malloc / free或新建/刪除)分配內(nèi)存,這樣的分配是不計(jì)入對24 MB的限制 。這是真的,從本機(jī)代碼分配內(nèi)存是為從Java方便,但它可以被用來存儲(chǔ)在RAM中的數(shù)據(jù)(即使圖像數(shù)據(jù))的一些大金額 。 另一種方式,其中的作品以及圖像的,是使用OpenGL的紋理-紋理內(nèi)存不計(jì)入限制 ,要查看您的應(yīng)用程序確實(shí)分配多少內(nèi)存可以使用android.os.Debug.getNativeHeapAllocatedSize( ),可以使用上面介紹的兩種技術(shù)的Nexus之一,我可以輕松地為一個(gè)單一的前臺(tái)進(jìn)程分配300MB - 10倍以上的默認(rèn)24 MB的限制 ,從上面來看使用navtive代碼分配內(nèi)存是不在24MB的限制內(nèi)的(開放的GL的質(zhì)地也是使用navtive代碼分配內(nèi)存的) 。 |
每個(gè) android 平臺(tái)內(nèi)存限制不一樣,從最開始的 16M 到 24M,以及后來的 32M,64M,或許以后會(huì)更大。
那如何獲取單個(gè) app 內(nèi)存限制大小呢?
class : ActivityManager
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
activityManager.getMemoryClass();
當(dāng)然,ActivityManager 不單單限與此,許多對 android 程序管理的工具,都來源與此,或者從這里進(jìn)行擴(kuò)展。
android不同設(shè)備單個(gè)進(jìn)程可用內(nèi)存是不一樣的,可以查看/system/build.prop文件。
dalvik.vm.heapstartsize=5m
dalvik.vm.heapgrowthlimit=48m
dalvik.vm.heapsize=256m
heapsize參數(shù)表示單個(gè)進(jìn)程可用的最大內(nèi)存,但如果存在如下參數(shù):
dalvik.vm.heapgrowthlimit=48m表示單個(gè)進(jìn)程內(nèi)存被限定在48m,即程序運(yùn)行過程中實(shí)際只能使用48m內(nèi)存
android上的應(yīng)用是java,當(dāng)然需要虛擬機(jī),而android上的應(yīng)用是帶有獨(dú)立虛擬機(jī)的,也就是每開一個(gè)應(yīng)用就會(huì)打開一個(gè)獨(dú)立的虛擬機(jī)。這樣設(shè)計(jì)的原因是可以避免虛擬機(jī)崩潰導(dǎo)致整個(gè)系統(tǒng)崩潰,但代價(jià)就是需要更多內(nèi)存。以上這些設(shè)計(jì)確保了android的穩(wěn)定性,正常情況下最多單個(gè)程序崩潰,但整個(gè)系統(tǒng)不會(huì)崩潰,也永遠(yuǎn)沒有內(nèi)存不足的提示出現(xiàn)。
在Android中,一個(gè)Process 只能使用16M內(nèi)存(?),要是超過了這個(gè)限定就會(huì)跳出這個(gè)異常
For Android specific we should use the 'recycle' method rather than 'gc', because 'recycle' will free the memory at the same time, but calling 'gc' doesn't guaranty to run and free the memory for same time(if it is not too critical, we should not call gc in our code) and results can very every time.
One more thing using 'recycle' is faster than the 'gc' and it improves the performance.
即:bitmap.recycle();
biamap=null;
效果要好于
biamap=null;
system.gc();
通過DDMS中的Heap選項(xiàng)卡監(jiān)視內(nèi)存情況:
1.Heap視圖中部有一個(gè)Type叫做data object,即數(shù)據(jù)對象,也就是我們的程序中大量存在的類類型的對象。
2.在data object一行中有一列是“Total Size”,其值就是當(dāng)前進(jìn)程中所有Java數(shù)據(jù)對象的內(nèi)存總量。
如果代碼中存在沒有釋放對象引用的情況,則data object的Total Size值在每次GC后不會(huì)有明顯的回落,隨著操作次數(shù)的增多Total Size的值會(huì)越來越大,
直到到達(dá)一個(gè)上限后導(dǎo)致進(jìn)程被kill掉。
2 今天剛遇到的情況:發(fā)現(xiàn)gridview的getview中使用
@Override public View getView(int position, View convertView, ViewGroup parent) { final View GridItem = mInflater.inflate(R.layout.store_catg_item,null, false); TextView text = (TextView) GridItem.findViewById(R.id.store_catg_item_text); ImageView cover = (ImageView) GridItem.findViewById(R.id.store_catg_item_cover); Bitmap coverimg = ImageUtilities.getCachedCover(magaList .get(position).id+ReaderConfigures.THUMB_SUFFIX_PLANE); String title; if(isCatg){ title= magaList.get(position).category; text.setText(title.toUpperCase()); }else{ title= magaList.get(position).pubname; text.setVisibility(View.INVISIBLE); } GridItem.setTag(title); cover.setImageBitmap(coverimg); return GridItem; }
滑動(dòng)時(shí)內(nèi)存會(huì)不斷漲,直到OutOfMemory,使用Holder后便不會(huì)發(fā)生該請況,具體原因未仔細(xì)查找,標(biāo)記一下。
1.對于常規(guī)開發(fā)者而言需要了解 Java的四種引用方式,比如強(qiáng)引用,軟引用,弱引用以及虛引用。一些復(fù)雜些的程序在長期運(yùn)行很可能出現(xiàn)類似OutOfMemoryError的異常。
2.并不要過多的指望gc,不用的對象可以顯示的設(shè)置為空,比如obj=null,java的gc使用的是一個(gè)有向圖,判斷一個(gè)對象是否有效看的是其他的對象能到達(dá)這個(gè)對象的頂點(diǎn),有向圖的相對于鏈表、二叉樹來說開銷是可想而知。
3.Android為每個(gè)程序分配的對內(nèi)存可以通過Runtime類的totalMemory() freeMemory() 兩個(gè)方法獲取VM的一些內(nèi)存信息,
Runtime.getRuntime().freeMemory();
Formatter.formatFileSize(BaseActivity.baseContext,Runtime.getRuntime().freeMemory()));//格式化輸出
對于系統(tǒng)heap內(nèi)存獲取,可以通過Dalvik.VMRuntime類的getMinimumHeapSize() 方法獲取最小可用堆內(nèi)存,同時(shí)顯示釋放軟引用可以調(diào)用該類的gcSoftReferences() 方法,獲取更多的運(yùn)行內(nèi)存。
4.對于多線程的處理,如果并發(fā)的線程很多,同時(shí)有頻繁的創(chuàng)建和釋放,可以通過concurrent類的線程池解決線程創(chuàng)建的效率瓶頸。
5. 不要在循環(huán)中創(chuàng)建過多的本地變量。
3.
The default heap size of android3.0 is 48M.Large background pictrue,button icon and the other pictrues used as ui all consume memory,and even if you have entered another activity,the resource of the previous activity still be keeped.So you had better not use the big pictrue in UI.
在onDestroy中會(huì)用((BitmapDrawable)mBtn.getBackground()).setCallback(null)清理背景圖。按道理來說圖片資源應(yīng)該已經(jīng)清理掉了的。仔細(xì)看Bitmap的源代碼,它其實(shí)起的作用是銷毀java對象BitmapDrawable,而android為了提高效率,Bitmap真正的位圖數(shù)據(jù)是在ndk中用c寫的,所以用setCallback是不能銷毀位圖數(shù)據(jù)的,應(yīng)該調(diào)用Bitmap的recycle()來清理內(nèi)存。在onDestroy加上((BitmapDrawable)mBtn.getBackground()).getBitmap().recycle(),這樣跑下來,內(nèi)存情況很理想,不管在哪個(gè)activity中,使用的資源僅僅是當(dāng)前activity用到的,就不會(huì)象之前到最后一個(gè)activity的時(shí)候,所有之前使用的資源都累積在內(nèi)存中。
但新的問題又出現(xiàn)了,當(dāng)返回之前的activity時(shí),會(huì)出現(xiàn)“try to use a recycled bitmap"的異常。這真是按了葫蘆起了瓢啊,內(nèi)心那個(gè)沮喪。。。沒辦法,繼續(xù)分析。看來是后加上recycle引起的, 位圖肯定在內(nèi)存中有引用,在返回之前的activity時(shí),因?yàn)槲粓D數(shù)據(jù)其實(shí)已經(jīng)被銷毀了,所以才造成目前的情況。在看了setBackgroundResource的源碼以后,恍然大悟,android對于直接通過資源id載入的資源其實(shí)是做了cache的了,這樣下次再需要此資源的時(shí)候直接從cache中得到,這也是為效率考慮。但這樣做也造成了用過的資源都會(huì)在內(nèi)存中,這樣的設(shè)計(jì)不是很適合使用了很多大圖片資源的應(yīng)用,這樣累積下來應(yīng)用的內(nèi)存峰值是很高的。看了sdk后,我用:
Bitmap bm = BitmapFactory.decodeResource(this.getResources(), R.drawable.splash);
BitmapDrawable bd = new BitmapDrawable(this.getResources(), bm);
mBtn.setBackgroundDrawable(bd);
來代替mBtn.setBackgroundResource(R.drawable.splash)。
銷毀的時(shí)候使用:
BitmapDrawable bd = (BitmapDrawable)mBtn.getBackground();
mBtn.setBackgroundResource(0);//別忘了把背景設(shè)為null,避免onDraw刷新背景時(shí)候出現(xiàn)used a recycled bitmap錯(cuò)誤
bd.setCallback(null);
bd.getBitmap().recycle();
這樣調(diào)整后,避免了在應(yīng)用里緩存所有的資源,節(jié)省了寶貴的內(nèi)存,而其實(shí)這樣也不會(huì)造成太大效率問題,畢竟重新載入資源是非常快速,不會(huì)對性能造成很嚴(yán)重的影響,在xoom里我沒有感受到和之前有什么區(qū)別。
總之,在android上使用大量位圖是個(gè)比較痛苦的事,內(nèi)存限制的存在對應(yīng)用是個(gè)很大的瓶頸。但不用因噎費(fèi)食,其實(shí)弄明白了它里面的機(jī)制,應(yīng)用可以突破這些限制的。這只是其中的一種處理方法,還可以考慮BitmapFactory.Options的inSampleSize來減少內(nèi)存占用。
瀏覽大圖的應(yīng)用,可以使用JNI的方法加載圖片
引用:http://www.cnblogs.com/qiengo/archive/2012/04/25/2468372.html