android Memory Management, OutOfMemoryError Note
1
Android框架強制每個進程的24 MB內(nèi)存限制。在一些舊的設(shè)備,如在G1,限制為16 MB 更低,更重要的是,由位圖使用的內(nèi)存限制。處理圖像的應(yīng)用程序,它是很容易達到此限制,并獲得與OOM 異常死亡 的過程:E / dalvikvm堆(12517):1048576字節(jié)外部分配這個 過程中過大的E / GraphicsJNI(12517): VM將不會讓我們分配1048576字節(jié) / AndroidRuntime(12517):關(guān)閉VM / dalvikvm(12517):主題ID = 1:線程未捕獲的異常退出(集團= 0x4001d7f0 ) E / AndroidRuntime(12517):致命異常:主要 電子/ AndroidRuntime(12517):java.lang.OutOfMemoryError:位圖的大小超過VM的預(yù)算 ,這個限制是低得離譜 。設(shè)備,像512MB的物理RAM的Nexus之一,設(shè)置每個進程的前臺活動只有5%的RAM的內(nèi)存限制是一個愚蠢的錯誤 。但無論如何,事情是如何和我們生活-即找到如何解決它。 遠(yuǎn)遠(yuǎn)超過限制的內(nèi)存分配方式有兩種 : 一種方法是從本機代碼分配內(nèi)存 。使用NDK(本地開發(fā)工具包)和JNI,它可能從C級(如的malloc / free或新建/刪除)分配內(nèi)存,這樣的分配是不計入對24 MB的限制 。這是真的,從本機代碼分配內(nèi)存是為從Java方便,但它可以被用來存儲在RAM中的數(shù)據(jù)(即使圖像數(shù)據(jù))的一些大金額 。 另一種方式,其中的作品以及圖像的,是使用OpenGL的紋理-紋理內(nèi)存不計入限制 ,要查看您的應(yīng)用程序確實分配多少內(nèi)存可以使用android.os.Debug.getNativeHeapAllocatedSize( ),可以使用上面介紹的兩種技術(shù)的Nexus之一,我可以輕松地為一個單一的前臺進程分配300MB - 10倍以上的默認(rèn)24 MB的限制 ,從上面來看使用navtive代碼分配內(nèi)存是不在24MB的限制內(nèi)的(開放的GL的質(zhì)地也是使用navtive代碼分配內(nèi)存的) 。 |
每個 android 平臺內(nèi)存限制不一樣,從最開始的 16M 到 24M,以及后來的 32M,64M,或許以后會更大。
那如何獲取單個 app 內(nèi)存限制大小呢?
class : ActivityManager
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
activityManager.getMemoryClass();
當(dāng)然,ActivityManager 不單單限與此,許多對 android 程序管理的工具,都來源與此,或者從這里進行擴展。
android不同設(shè)備單個進程可用內(nèi)存是不一樣的,可以查看/system/build.prop文件。
dalvik.vm.heapstartsize=5m
dalvik.vm.heapgrowthlimit=48m
dalvik.vm.heapsize=256m
heapsize參數(shù)表示單個進程可用的最大內(nèi)存,但如果存在如下參數(shù):
dalvik.vm.heapgrowthlimit=48m表示單個進程內(nèi)存被限定在48m,即程序運行過程中實際只能使用48m內(nèi)存
android上的應(yīng)用是java,當(dāng)然需要虛擬機,而android上的應(yīng)用是帶有獨立虛擬機的,也就是每開一個應(yīng)用就會打開一個獨立的虛擬機。這樣設(shè)計的原因是可以避免虛擬機崩潰導(dǎo)致整個系統(tǒng)崩潰,但代價就是需要更多內(nèi)存。以上這些設(shè)計確保了android的穩(wěn)定性,正常情況下最多單個程序崩潰,但整個系統(tǒng)不會崩潰,也永遠(yuǎn)沒有內(nèi)存不足的提示出現(xiàn)。
在Android中,一個Process 只能使用16M內(nèi)存(?),要是超過了這個限定就會跳出這個異常
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選項卡監(jiān)視內(nèi)存情況:
1.Heap視圖中部有一個Type叫做data object,即數(shù)據(jù)對象,也就是我們的程序中大量存在的類類型的對象。
2.在data object一行中有一列是“Total Size”,其值就是當(dāng)前進程中所有Java數(shù)據(jù)對象的內(nèi)存總量。
如果代碼中存在沒有釋放對象引用的情況,則data object的Total Size值在每次GC后不會有明顯的回落,隨著操作次數(shù)的增多Total Size的值會越來越大,
直到到達一個上限后導(dǎo)致進程被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; }
滑動時內(nèi)存會不斷漲,直到OutOfMemory,使用Holder后便不會發(fā)生該請況,具體原因未仔細(xì)查找,標(biāo)記一下。
1.對于常規(guī)開發(fā)者而言需要了解 Java的四種引用方式,比如強引用,軟引用,弱引用以及虛引用。一些復(fù)雜些的程序在長期運行很可能出現(xiàn)類似OutOfMemoryError的異常。
2.并不要過多的指望gc,不用的對象可以顯示的設(shè)置為空,比如obj=null,java的gc使用的是一個有向圖,判斷一個對象是否有效看的是其他的對象能到達這個對象的頂點,有向圖的相對于鏈表、二叉樹來說開銷是可想而知。
3.Android為每個程序分配的對內(nèi)存可以通過Runtime類的totalMemory() freeMemory() 兩個方法獲取VM的一些內(nèi)存信息,
Runtime.getRuntime().freeMemory();
Formatter.formatFileSize(BaseActivity.baseContext,Runtime.getRuntime().freeMemory()));//格式化輸出
對于系統(tǒng)heap內(nèi)存獲取,可以通過Dalvik.VMRuntime類的getMinimumHeapSize() 方法獲取最小可用堆內(nèi)存,同時顯示釋放軟引用可以調(diào)用該類的gcSoftReferences() 方法,獲取更多的運行內(nèi)存。
4.對于多線程的處理,如果并發(fā)的線程很多,同時有頻繁的創(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中會用((BitmapDrawable)mBtn.getBackground()).setCallback(null)清理背景圖。按道理來說圖片資源應(yīng)該已經(jīng)清理掉了的。仔細(xì)看Bitmap的源代碼,它其實起的作用是銷毀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)存情況很理想,不管在哪個activity中,使用的資源僅僅是當(dāng)前activity用到的,就不會象之前到最后一個activity的時候,所有之前使用的資源都累積在內(nèi)存中。
但新的問題又出現(xiàn)了,當(dāng)返回之前的activity時,會出現(xiàn)“try to use a recycled bitmap"的異常。這真是按了葫蘆起了瓢啊,內(nèi)心那個沮喪。。。沒辦法,繼續(xù)分析。看來是后加上recycle引起的, 位圖肯定在內(nèi)存中有引用,在返回之前的activity時,因為位圖數(shù)據(jù)其實已經(jīng)被銷毀了,所以才造成目前的情況。在看了setBackgroundResource的源碼以后,恍然大悟,android對于直接通過資源id載入的資源其實是做了cache的了,這樣下次再需要此資源的時候直接從cache中得到,這也是為效率考慮。但這樣做也造成了用過的資源都會在內(nèi)存中,這樣的設(shè)計不是很適合使用了很多大圖片資源的應(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)。
銷毀的時候使用:
BitmapDrawable bd = (BitmapDrawable)mBtn.getBackground();
mBtn.setBackgroundResource(0);//別忘了把背景設(shè)為null,避免onDraw刷新背景時候出現(xiàn)used a recycled bitmap錯誤
bd.setCallback(null);
bd.getBitmap().recycle();
這樣調(diào)整后,避免了在應(yīng)用里緩存所有的資源,節(jié)省了寶貴的內(nèi)存,而其實這樣也不會造成太大效率問題,畢竟重新載入資源是非常快速,不會對性能造成很嚴(yán)重的影響,在xoom里我沒有感受到和之前有什么區(qū)別。
總之,在android上使用大量位圖是個比較痛苦的事,內(nèi)存限制的存在對應(yīng)用是個很大的瓶頸。但不用因噎費食,其實弄明白了它里面的機制,應(yīng)用可以突破這些限制的。這只是其中的一種處理方法,還可以考慮BitmapFactory.Options的inSampleSize來減少內(nèi)存占用。
瀏覽大圖的應(yīng)用,可以使用JNI的方法加載圖片
引用:http://www.cnblogs.com/qiengo/archive/2012/04/25/2468372.html