2012-05-20 63 views
28

我對Android開發相當陌生,似乎無法掌握Java內存異常。我知道這意味着我的應用程序已經超出了虛擬機的預算,但經過Google多次搜索之後,我似乎仍然無法理解這一概念。恐怕我的應用使用了太多內存,因爲每個屏幕有六個按鈕選擇器,每個選擇器有兩個位圖,根據屬性選項卡,每個選擇器大約20 kb。在我根深蒂固的G2x上,我將虛擬機預算設置爲12MB,重新啓動手機並運行我的應用程序,無任何問題。我在每個onDestroy()上解綁定drawables,暗示GC也在這裏運行。在模擬器中使用該應用程序一段時間後,我在DDMS屏幕上單擊「原因GC」,結果爲 ID = 1,堆大小6.133 MB,分配2.895MB,免費3.238 MB,%使用47.20,#對象52,623。Android瞭解堆大小

這是我不明白髮生了什麼,我的模擬器設置爲24MB的虛擬機。那個號碼在哪裏?我遇到的實際問題是如果我將模擬器設置爲16MB的虛擬機,那麼我的應用程序會在第二個活動出現內存不足異常時崩潰。爲什麼它不會在VM設置爲12 MB的情況下在我的手機上崩潰,或者在帶有12 MB VM的舊HTC Magic手機上崩潰?你們也認爲我的應用佔用了太多的內存嗎?我不知道這些DDMS數字是否好。謝謝你的時間。

至於我的代碼,我已經在XML佈局中指定了每個圖像,除了向它們添加偵聽器之外,我不會對它們進行編程。我發現這段代碼在這裏,我已經將其添加到我的每一次活動......

@Override 
protected void onDestroy() { 
    super.onDestroy(); 

    unbindDrawables(findViewById(R.id.myRootLayout)); 
    System.gc(); 
} 

private void unbindDrawables(View view) { 
    if (view.getBackground() != null) { 
     view.getBackground().setCallback(null); 
    } 
    if (view instanceof ViewGroup && !(view instanceof AdapterView)) { 
     for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) { 
      unbindDrawables(((ViewGroup) view).getChildAt(i)); 
     } 
     ((ViewGroup) view).removeAllViews(); 
    } 
} 

否則我要做的就是添加onClickListeners到具有PNG背景的按鈕。我想了解如何以編程方式指定按鈕背景,但我需要讓選擇器功能像聚焦,按下,非聚焦但按下等,以使按鈕背景根據用戶交互而改變。我已閱讀關於此的文檔,但似乎壓倒性的,這就是爲什麼我想我會從這裏開始管理堆的基本知識,並努力工作,直到在代碼中指定選擇器。這可能沒有道理,但應用程序可以分配內存分配的「健康」數量,而不會接近內存不足異常嗎?例如,如果一個應用程序分配了6MB,應該沒問題,但是8MB會推動它,在內存分配方面是否有這樣的限制?再次感謝亞歷克斯洛克伍德您的迴應我將閱讀並重新閱讀它,直到這個東西對我有意義

回答

50

當您在模擬器/設備上設置虛擬機預算,你在做什麼是告訴堆它允許的最大尺寸。在運行時,隨着Dalvik VM從操作系統請求系統內存,堆的大小會動態增長。 Dalvik VM通常從分配相對較小的堆開始。然後在每次GC運行後,檢查是否有多少空閒堆內存。如果可用堆與總堆的比例太小,則Dalvik VM會向堆中添加更多內存(直至配置的最大堆大小)。

這就是說,你在DDMS屏幕上看不到「24 MB」的原因是因爲堆沒有增長到最大尺寸。這使Android可以充分利用手持設備上已有的少量內存。至於爲什麼你的應用程序在模擬器而不是你的手機上崩潰,那看起來很奇怪(你確定這些數字是正確的嗎?)。但是,您應該記住,內存是動態管理的,並且總內存利用率是根據許多外部因素(執行垃圾收集的速度/頻率等)確定的。

最後,由於我上面提到的原因,很難確定您的應用程序如何根據上面提供的單行信息管理內存。我們真的需要看到你的一些代碼。但是,絕對值得擔心,所以我肯定會研究你的應用程序的內存使用情況。您可能會考慮的一件事是在運行時使用BitmapFactory類對inSampleSize進行調用來對您的位圖圖像進行採樣。這可以幫助減少加載可繪製位圖所需的內存量。無論是或者你可以減少你的drawables的分辨率(儘管20 kb的每個對我來說聽起來都很好)。

+3

+1,非常豐富的答案! –

+0

非常感謝!我從中學到了很多東西。我在問題的底部添加了一些信息 –

+0

重新解析評論「20 kb,每個聽起來都很好」 - AFAIK,*文件大小*無關緊要(對於壓縮更好的圖像,這將更小),它關乎*尺寸*是什麼;在內存中,對於全綵色,每像素需要4B,對吧?因此,根據當前設備的實際所需大小加載它時調整圖像的大小非常重要[android docs - 將縮小的版本加載到內存中]。 – ToolmakerSteve

16

即使您的應用程序沒有達到「24mb」(設備內的不同)堆限制,您仍然可能會崩潰,因爲Android需要一段時間才能爲您的應用程序增加堆空間。

在我的情況下,我在少量時間內創建並傾倒了幾張圖片。

很多時候我得到了OutOfMemoryError

看來Android的速度不夠快,無法爲我的應用增加堆空間。

我相信我通過在Manifest文件中使用largeHeap設置解決了這個問題。 隨着該設置的開啓,Android每次增長堆時都會留下更多可用內存,從而最大限度地降低了達到當前限制的可能性。

我不使用24mb的限制,但這個largeHeap conf是相當方便的。

你只需要設置largeHeap="true"對你AndroidManifest.xml

<application 
    android:icon="@drawable/ic_launcher" 
    android:label="@string/app_name" 
    android:largeHeap="true" 
    android:theme="@style/AppTheme" > 

應用程序標記儘管如此,確保與圖片的時候,像@Alex洛克伍德勸你小心。

+16

儘可能小心使用largeHeap =「true」。因爲使用這可能會不利地影響您的應用程序性能。因爲您告訴系統增加最大堆限制。發生這種情況時,垃圾收集需要更多時間。如果您檢查日誌,您可以看到GC暫停時間會更長。理想情況下,它應該在2-5ms之間。在這種情況下,甚至可以高達30-45ms。所以,不要因爲你的內存不足而將大堆屬性設置爲true。將它用作最後一步。否則,它將成爲性能打擊。 –

+0

確實。 在我的情況下,我正在動態處理大量高質量圖像,並且在使用largeHeap選項之前,我已經實現了以下代碼: - 加載圖像採樣 - 將圖像調整爲完美匹配 - 清除內存中未使用的圖像 - 在「磁盤」中緩存圖像 我仍然遇到只有使用largeHeap選項解決的問題。 不過,只有在嘗試其他所有內容後才能使用它,例如@SanalVarghese建議。 – tbraun

+0

只是增加了這一點,我一直有大量的問題與我的應用程序的內存它的大小大大增加。在這一點上它可能不會有效地集合在一起,但它本應該能夠不能達到最大限度。但它永遠不會釋放許多資源。 由於某些原因,它在應用largeHeap選項後很好。謝謝一堆。 –