Android Memory Management, OutOfMemoryError


Android Memory Management, OutOfMemoryError

   Android框 架強制每個進程的24 MB內存限制。在一些舊的設備,如在G1,限制爲16 MB 更低,更重要的是,由位圖使用的內存限制。處理圖像的應用程序,它是很容易達到此限制,並獲得與OOM 異常死亡 的過程:E / dalvikvm堆(12517):1048576字節外部分配這個 過程中過大的E / GraphicsJNI(12517): VM將不會讓我們分配1048576字節 / AndroidRuntime(12517):關閉VM / dalvikvm(12517):主題ID = 1:線程未捕獲的異常退出(集團= 0x4001d7f0 ) E / AndroidRuntime(12517):致命異常:主要 電子/ AndroidRuntime(12517):java.lang.OutOfMemoryError:位圖的大小超過VM的預算 ,這個限制是低得離譜 。設備,像512MB的物理RAM的Nexus之一,設置每個進程的前臺活動只有5%的RAM的內存限制是一個愚蠢的錯誤 。但無論如何,事情是如何和我們生活-即找到如何解決它。

遠遠超過限制的內存分配方式有兩種 :

  一種方法是從本機代碼分配內存 。使用NDK(本地開發工具包)和JNI,它可能從C級(如的malloc / free或新建/刪除)分配內存,這樣的分配是不計入對24 MB的限制 。這是真的,從本機代碼分配內存是爲從Java方便,但它可以被用來存儲在RAM中的數據(即使圖像數據)的一些大金額 。

  另一種方式,其中的作品以及圖像的,是使用OpenGL的紋理-紋理內存不計入限制 ,要查看您的應用程序確實分配多少內存可以使用android.os.Debug.getNativeHeapAllocatedSize( ),可以使用上面介紹的兩種技術的Nexus之一,我可以輕鬆地爲一個單一的前臺進程分配300MB - 10倍以上的默認24 MB的限制 ,從上面來看使用navtive代碼分配內存是不在24MB的限制內的(開放的GL的質地也是使用navtive代碼分配內存的) 。

  每個 android 平臺內存限制不一樣,從最開始的 16M 到 24M,以及後來的 32M,64M,或許以後會更大。

  那如何獲取單個 app 內存限制大小呢?

  class : ActivityManager

ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
activityManager.getMemoryClass();

  當然,ActivityManager 不單單限與此,許多對 android 程序管理的工具,都來源與此,或者從這裏進行擴展。

 

android不同設備單個進程可用內存是不一樣的,可以查看/system/build.prop文件。

dalvik.vm.heapstartsize=5m
dalvik.vm.heapgrowthlimit=48m
dalvik.vm.heapsize=256m

heapsize參數表示單個進程可用的最大內存,但如果存在如下參數:

dalvik.vm.heapgrowthlimit=48m表示單個進程內存被限定在48m,即程序運行過程中實際只能使用48m內存

android上的應用是java,當然需要虛擬機,而android上的應用是帶有獨立虛擬機的,也就是每開一個應用就會打開一個獨立的虛擬機。這樣設計的原因是可以避免虛擬機崩潰導致整個系統崩潰,但代價就是需要更多內存。以上這些設計確保了android的穩定性,正常情況下最多單個程序崩潰,但整個系統不會崩潰,也永遠沒有內存不足的提示出現。

 在Android中,一個Process 只能使用16M內存(?),要是超過了這個限定就會跳出這個異常

  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選項卡監視內存情況:

1.Heap視圖中部有一個Type叫做data object,即數據對象,也就是我們的程序中大量存在的類類型的對象。

2.在data object一行中有一列是“Total Size”,其值就是當前進程中所有Java數據對象的內存總量。

如果代碼中存在沒有釋放對象引用的情況,則data object的Total Size值在每次GC後不會有明顯的回落,隨着操作次數的增多Total Size的值會越來越大,
  直到到達一個上限後導致進程被kill掉。

B  今天剛遇到的情況:發現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;
        }

複製代碼

 

滑動時內存會不斷漲,直到OutOfMemory,使用Holder後便不會發生該請況,具體原因未仔細查找,標記一下。

 1.對於常規開發者而言需要了解 Java的四種引用方式,比如強引用,軟引用,弱引用以及虛引用。一些複雜些的程序在長期運行很可能出現類似OutOfMemoryError的異常。

2.並不要過多的指望gc,不用的對象可以顯示的設置爲空,比如obj=null,java的gc使用的是一個有向圖,判斷一個對象是否有效看的是其他的對象能到達這個對象的頂點,有向圖的相對於鏈表、二叉樹來說開銷是可想而知。

3.Android爲每個程序分配的對內存可以通過Runtime類的totalMemory() freeMemory() 兩個方法獲取VM的一些內存信息,

Runtime.getRuntime().freeMemory();

Formatter.formatFileSize(BaseActivity.baseContext,Runtime.getRuntime().freeMemory()));//格式化輸出

對於系統heap內存獲取,可以通過Dalvik.VMRuntime類的getMinimumHeapSize() 方法獲取最小可用堆內存,同時顯示釋放軟引用可以調用該類的gcSoftReferences() 方法,獲取更多的運行內存。

4.對於多線程的處理,如果併發的線程很多,同時有頻繁的創建和釋放,可以通過concurrent類的線程池解決線程創建的效率瓶頸。

5. 不要在循環中創建過多的本地變量。

c

  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)清理背景圖。按道理來說圖片資源應 該已經清理掉了的。仔細看Bitmap的源代碼,它其實起的作用是銷燬java對象BitmapDrawable,而android爲了提高效率,Bitmap真正的位圖數據是在ndk中用c寫的,所以用setCallback是不能銷燬位圖數據的,應該調用Bitmap的recycle()來清理內存。在onDestroy加上((BitmapDrawable)mBtn.getBackground()).getBitmap().recycle(),這樣跑下來,內存情況很理想,不管在哪個activity中,使用的資源僅僅是當前activity用到的,就不會象之前到最後一個activity的時候,所有之前使用的資源都累積在內存中。

  但新的問題又出現了,當返回之前的activity時,會出現“try to use a recycled bitmap"的異常。這真是按了葫蘆起了瓢啊,內心那個沮喪。。。沒辦法,繼續分析。看來是後加上recycle引起的, 位圖肯定在內存中有引用,在返回之前的activity時,因爲位圖數據其實已經被銷燬了,所以才造成目前的情況。在看了 setBackgroundResource的源碼以後,恍然大悟,android對於直接通過資源id載入的資源其實是做了cache的了,這樣下次再 需要此資源的時候直接從cache中得到,這也是爲效率考慮。但這樣做也造成了用過的資源都會在內存中,這樣的設計不是很適合使用了很多大圖片資源的應 用,這樣累積下來應用的內存峯值是很高的。看了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);//別忘了把背景設爲null,避免onDraw刷新背景時候出現used a recycled bitmap錯誤

bd.setCallback(null);
bd.getBitmap().recycle();
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章