Out Of Memory Error in Android – part 1

來源:互聯網
上載者:User

之前這篇,說的不是太詳細,今兒再來詳細的說說:

 

1.什麼是 OutOfMemoryError:

   官方引用: Thrown when a request for memory is made that can not be satisfied using the available platform resources. Such a request may be made by both the running application or by an internal function of the VM.
   通俗的講:就是在請求一塊記憶體的時候,當前可用資源不夠用來請求時拋出的一種錯誤。我們知道,每個 android 程式就是一個獨立 dalvik vm 執行個體,每個執行個體限制了最大記憶體佔用,如果超過了這個限制,系統就會拋出這個錯誤。所以跟整個裝置的剩餘記憶體沒太大關係,當然如果裝置剩餘記憶體都不足以再運行一個程式時,系統就會選擇 kill 部分程式以確保有足夠記憶體運行其他程式。

 

2.android 記憶體組成:

   android 記憶體由 dalvik 和 native 2部分組成,dalvik 也就是 java 堆,建立的對象就是在這裡分配的,而 native 是通過 c/c++ 方式申請的記憶體,Bitmap 就是以一種方式分配的(android3.0 以後,系統預設是通過 dalvik 分配的)。當然無論以何種方式分配,2部分加起來不能超過 android 對單個程式的記憶體限制。

 

3.記憶體限制大小:

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

 以上方法會返回以 M 為單位的數字,可能在不同的平台或者裝置上值都不太一樣,比如:HTC G7 預設 24M,Galaxy 36M,emulator-2.3 24M,等等。

 

4.程式實際佔用:

  以一個簡單的 android 程式為例,該程式是用 eclipse adt 自動產生的最簡單的一個 android 項目,只有1個 activity 和 adt 自動產生的 res 目錄,測試環境:emulator-2.3.3
  啟動該程式,命令列運行:

 

adb shell dumpsys meminfo com.mem.demo

 

  執行結果:

 

Applications Memory Usage (kB):Uptime: 1195344 Realtime: 1195344** MEMINFO in pid 333 [com.mem.demo] **                    native   dalvik    other    total    --------------------------------------------------------    |       size:     3968     5379      N/A     9347      |    |                                                      |    |   allocated:    3964     2649      N/A     6613      |    --------------------------------------------------------            free:        3     2730      N/A     2733           (Pss):      553      449     2516     3518  (shared dirty):     2272     1868     6648    10788    (priv dirty):      420       32     1140     1592  Objects           Views:        0        ViewRoots:        0     AppContexts:        0       Activities:        0          Assets:        2    AssetManagers:        2   Local Binders:        5    Proxy Binders:       10Death Recipients:        0 OpenSSL Sockets:        0  SQL               heap:        0         MEMORY_USED:        0 PAGECACHE_OVERFLOW:        0         MALLOC_SIZE:        0   Asset Allocations    zip:/data/app/com.mem.demo-1.apk:/resources.arsc: 1K

 

從上面被框出來的部分可以看出,一個最簡單的 android 程式在啟動後都有 6m 左右記憶體的佔用(上面是 6613kb)。那這 6m 的記憶體除了該 android 自己的資源和類之外,其他的還有什麼呢:

簡單說:在初始化的時候會 preload 一些東西,這些就包括 classes 和系統資源,就是系統的一些布局啊,圖片啊,等等,在 android 完成啟動以後,這部分就通過記憶體共用的方式共用給其他程式,可以讓其他程式可以調用這部分資源,代碼可以參考:http://goo.gl/EKvCV,android 整個啟動流程可以參考:http://goo.gl/K36Lr 。

 

5.發生 OOM :

 為了製造 OOM,我們對上面最簡單的程式進行了改寫:

package com.mem.demo;import android.app.Activity;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.os.Bundle;public class DemoActivity extends Activity {    Bitmap map1, map2, map3, map4;    /** Called when the activity is first created. */    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.main);        map1 = BitmapFactory.decodeResource(getResources(), R.drawable.big1);        map2 = BitmapFactory.decodeResource(getResources(), R.drawable.big2);        map3 = BitmapFactory.decodeResource(getResources(), R.drawable.big3);        map4 = BitmapFactory.decodeResource(getResources(), R.drawable.big4);    }}

 

其中:big1 到 big4 都是 1600*900 解析度的圖片,放在 drawbable-hdpi 檔案夾下面,啟動程式,發生了 OOM:

 

05-08 07:44:44.372: E/dalvikvm-heap(386): 5760000-byte external allocation too large for this process.05-08 07:44:44.412: I/dalvikvm-heap(386): Clamp target GC heap from 25.099MB to 24.000MB05-08 07:44:44.412: E/GraphicsJNI(386): VM won't let us allocate 5760000 bytes05-08 07:44:44.412: D/dalvikvm(386): GC_FOR_MALLOC freed 0K, 53% free 2548K/5379K, external 18500K/20548K, paused 36ms05-08 07:44:44.422: D/skia(386): --- decoder->decode returned false05-08 07:44:44.422: D/AndroidRuntime(386): Shutting down VM05-08 07:44:44.432: W/dalvikvm(386): threadid=1: thread exiting with uncaught exception (group=0x40015560)05-08 07:44:44.442: E/AndroidRuntime(386): FATAL EXCEPTION: main05-08 07:44:44.442: E/AndroidRuntime(386): java.lang.OutOfMemoryError: bitmap size exceeds VM budget05-08 07:44:44.442: E/AndroidRuntime(386):     at android.graphics.BitmapFactory.nativeDecodeAsset(Native Method)05-08 07:44:44.442: E/AndroidRuntime(386):     at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:460)05-08 07:44:44.442: E/AndroidRuntime(386):     at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:336)05-08 07:44:44.442: E/AndroidRuntime(386):     at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:359)05-08 07:44:44.442: E/AndroidRuntime(386):     at android.graphics.BitmapFactory.decodeResource(BitmapFactory.java:385)05-08 07:44:44.442: E/AndroidRuntime(386):     at com.mem.demo.DemoActivity.onCreate(DemoActivity.java:20)05-08 07:44:44.442: E/AndroidRuntime(386):     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)05-08 07:44:44.442: E/AndroidRuntime(386):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1611)05-08 07:44:44.442: E/AndroidRuntime(386):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1663)05-08 07:44:44.442: E/AndroidRuntime(386):     at android.app.ActivityThread.access$1500(ActivityThread.java:117)05-08 07:44:44.442: E/AndroidRuntime(386):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:931)05-08 07:44:44.442: E/AndroidRuntime(386):     at android.os.Handler.dispatchMessage(Handler.java:99)05-08 07:44:44.442: E/AndroidRuntime(386):     at android.os.Looper.loop(Looper.java:123)05-08 07:44:44.442: E/AndroidRuntime(386):     at android.app.ActivityThread.main(ActivityThread.java:3683)05-08 07:44:44.442: E/AndroidRuntime(386):     at java.lang.reflect.Method.invokeNative(Native Method)05-08 07:44:44.442: E/AndroidRuntime(386):     at java.lang.reflect.Method.invoke(Method.java:507)05-08 07:44:44.442: E/AndroidRuntime(386):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)05-08 07:44:44.442: E/AndroidRuntime(386):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)05-08 07:44:44.442: E/AndroidRuntime(386):     at dalvik.system.NativeStart.main(Native Method)

 

從日誌上看,記憶體不足發生在 decode big4 這個圖片的時候,我們對記憶體情況進行列印:

 

Applications Memory Usage (kB):Uptime: 958383 Realtime: 958383** MEMINFO in pid 386 [com.mem.demo] **                    native   dalvik    other    total            size:    25144     5379      N/A    30523       allocated:    20799     2614      N/A    23413            free:       60     2765      N/A     2825           (Pss):      494      426    18494    19414  (shared dirty):     2288     1876     5292     9456    (priv dirty):      360       28    17836    18224

 

從記憶體列印情況看出,當前已經分配了 23m 左右記憶體,同時結合 logcat 錯誤記錄檔,big4 這個圖片需要申請 5760000byte,即:5.76m 的記憶體,加起來就是 29m 左右記憶體,我們測試環境是 emulator2.3.3 預設是
24m,記憶體不足以申請這麼多,所以拋出了OOM。

那為什麼區區3,4張圖片就會讓 android 程式記憶體不足? 
裝置限制是一方面,像上面第3點說的,每個 android 裝置的記憶體限制不一樣,這個程式在模擬器上會有問題,在其他裝置上,比如:galaxy 就不會有問題。最主要的還是跟圖片所佔記憶體有關係,那麼一張圖片到底
佔用多少記憶體呢,java 沒有 c 的 sizeof() 函數,無法準確去量化這個數值,但是可以有粗略的計算方法:

 

寬 * 高 * 每個像素所佔的 bytes

 

寬度和高度這個很容易獲得,那每個像素所佔的 bytes 呢,這個主要取決於 decode 圖片的方式:

 

Bitmap.Config     ALPHA_8        Each pixel is stored as a single translucency (alpha) channel. Bitmap.Config     ARGB_4444      This field is deprecated. Because of the poor quality of this configuration, it is advised to use ARGB_8888 instead.  Bitmap.Config     ARGB_8888      Each pixel is stored on 4 bytes. Bitmap.Config     RGB_565        Each pixel is stored on 2 bytes and only the RGB channels are encoded: red is stored with 5 bits of precision (32 possible values), green is store                                 d with 6 bits of precision (64 possible values) and blue is stored with 5 bits of precision. 

 

以上是官方文檔對 Bitmap.Config 類的描述,所以,如果以 ARGB_8888 的方式 decode,那個每個像素佔用4個 bytes,而如果用 RGB_565 就佔用2個 bytes。
我們計算一下,在 2.3 以後,程式內建的圖片資源,都預設以 ARGB_8888 的方式,而在此以之前是以 RGB_565 的方式(不確定,待驗證),所以顏色會有損耗,典型的就是如果有漸層色的話,會出現光圈。
所以,計算如下:

 

1600 * 900 * 4 = 5760000

 

 這個 5760000 也就是上面 logcat 錯誤記錄檔裡面所提到的申請數字,當然在實際用命令列印出的記憶體情況上看,比這個數字要大,是因為這隻是圖片像素的記憶體,還有一些屬性,變數和類本身沒有計算在內。

 
 未完,待續 。。。

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.