OOM是Android Out Of Memory的縮寫,基於Android開發應用時,可能經常出現Out Of Memory
異常.
在Android中,一個Process 只能使用16M記憶體,要是超過了這個限定就會跳出這個異常。這樣就要求我們要時刻想著開釋資源。Java的回收工作是交給GC的,如何讓GC能即時的回收已經不用的對象,有許多辦法。
因為總記憶體的施用超過16M而引起OOM的情況,非常簡單,我就不繼續展開說。值當注意的是Bitmap在不用時,肯定是要recycle,不然OOM是非常容易出現的。
但是還會出現另外一種情況:明明還有許多記憶體,但是發生OOM了。
這類情況時常出現在產生Bitmap的時候。有興趣的可以試一下,在一個函數裡產生一個13m 的int數組。
再該函數結束後,按理說這個int數組應該已經被開釋了,或者說可以開釋,這個13M的空間應該可以空出來,
這個時候要是你繼續生白手起家的百萬富翁成一個10M的int數組是沒有問題的,反而產生一個4M的Bitmap就會跳出OOM。這個就奇怪了,為啥子10M的int夠空間,反而4M的Bitmap不敷呢?
這個問題困擾好久,在網上,國外各大論壇搜颳了好久,一般關於OOM的解釋和解決方案都是,如何讓GC儘快回收的代碼風格之類,並沒有現實的支出上面所說的情況的根源。
直到昨天在一個老外的blog上終於看到了這方面的解釋,我理解後歸納如下:
在Android中:
1.一個進程的記憶體可以由2個部門組成:java 施用記憶體 ,C 施用記憶體 ,這兩個記憶體的和必需小於16M,不然就會出現各人熟悉的OOM,這個就是熬頭種OOM的情況。
2.越發奇怪的是這個:一朝記憶體配置給Java後,以後這塊記憶體縱然開釋後,也只能給Java的施用,這個估計跟java虛擬機器裡把記憶體分成好幾塊進行緩衝的原因有關,反正C就別想用到這塊的記憶體了,所以要是Java突然佔用了一個大塊記憶體,縱然很快開釋了:
C能施用的記憶體 = 16M - Java某一瞬間佔在校大學生創業點子用的最大記憶體。
而Bitmap的產生是路程經過過程malloc進行記憶體配置的,佔用的是C的記憶體,這個也就說明了,上面所說的的4MBitmap無法產生的原因,因為在13M被Java用過後,剩下C能用的只有3M了。
leak window 按字面瞭解大概就是說一個表單泄漏了,也就是我們常說的記憶體流失,為什麼表單會泄漏呢?
02-29 10:07:51.410: ERROR/WindowManager(4236): Activity cn.bookall.android.zbook.reader.DocReader has leaked window android.widget.TextView@485348b0 that was originally added here
02-29 10:07:51.410: ERROR/WindowManager(4236): android.view.WindowLeaked: Activity cn.bookall.android.zbook.reader.DocReader has leaked window android.widget.TextView@485348b0 that was originally added here
02-29 10:07:51.410: ERROR/WindowManager(4236): at android.view.ViewRoot.<init>(ViewRoot.java:247)
...
02-29 10:07:51.410: ERROR/WindowManager(4236): at cn.bookall.android.zbook.reader.DocView.ShowPopWindowCenter(DocView.java:506)
...
02-29 10:07:51.600: ERROR/AndroidRuntime(4236): java.lang.IllegalArgumentException:View not attached to window manager
產生原因:
我們知道Android的每一個Activity都有個WindowManager表單管理器,同樣,構建在某個Activity之上的對話方塊、PopupWindow也有相應的WindowManager表單管理器。因為對話方塊、PopupWindown不能脫離Activity而單獨存在著,所以當某個Dialog或者某個PopupWindow正在顯示的時候我們去finish()了承載該Dialog(或PopupWindow)的Activity時,就會拋Window
Leaked異常了,因為這個Dialog(或PopupWindow)的WindowManager已經沒有誰可以附屬了,所以它的表單管理器已經泄漏了。
解決方案:
關閉(finish)某個Activity前,要確保附屬在上面的Dialog或PopupWindow已經關閉(dismiss)了。
@Override
public void onPause(){
super.onPause();
if(pw != null) {
pw.dismiss();
}
}