原來OOM的罪魁禍首是C程式碼---android out of memory(OOM)
一.
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: 10 Death 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 bytes 05-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 false 05-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 錯誤日誌裡面所提到的申請數字,當然在實際用命令列印出的記憶體情況上看,比這個數字要大,是因為這只是圖片畫素的記憶體,還有一些屬性,變數和類本身沒有計算在內。
二.基於Android開發多媒體和遊戲應用時,可能會挺經常出現Out Of Memory 異常 ,顧名思義這個異常是說你的記憶體不夠用或者耗盡了。
在Android中,一個Process 只能使用16M記憶體,如果超過了這個限制就會跳出這個異常。這樣就要求我們要時刻想著釋放資源。Java的回收工作是交給GC的,如何讓GC能及時的回收已經不是用的物件,這個裡面有很多技巧,大家可以google一下。
因為總記憶體的使用超過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了。
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: 10 Death 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 bytes 05-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 false 05-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 錯誤日誌裡面所提到的申請數字,當然在實際用命令列印出的記憶體情況上看,比這個數字要大,是因為這只是圖片畫素的記憶體,還有一些屬性,變數和類本身沒有計算在內。
二.基於Android開發多媒體和遊戲應用時,可能會挺經常出現Out Of Memory 異常 ,顧名思義這個異常是說你的記憶體不夠用或者耗盡了。
在Android中,一個Process 只能使用16M記憶體,如果超過了這個限制就會跳出這個異常。這樣就要求我們要時刻想著釋放資源。Java的回收工作是交給GC的,如何讓GC能及時的回收已經不是用的物件,這個裡面有很多技巧,大家可以google一下。
因為總記憶體的使用超過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了。
相關文章
- OOM(Out Of Memory)OOM
- OOM(Out Of Memory)是什麼?OOM
- OOM--OUT OF MEMORYOOM
- android 載入圖片輕鬆避免OOM(out of memory)AndroidOOM
- context canceled,誰是罪魁禍首?Context
- 明基併購西門子失敗 罪魁禍首是IT整合
- 灰色產業的陰影中,CDKey成了罪魁禍首產業
- OOMOOM
- econsultancy:20個拖慢網站速度罪魁禍首網站
- oom killerOOM
- 什麼原因才是阻礙Linux桌面發展的罪魁禍首Linux
- inmemory OOM了OOM
- Facebook遭遇有史以來最嚴重當機事件,罪魁禍首與DNS故障有關?事件DNS
- AIX OOM問題AIOOM
- Android 載入大圖片時報OOM的解決方案(原始碼)AndroidOOM原始碼
- Linux核心引數overcommit_memory和OOM killer介紹LinuxMITOOM
- JAVA各種OOM程式碼樣例及解決方法JavaOOM
- Android Web3j OOM解決AndroidWebOOM
- Android高階效能調優;不可思議的OOM!AndroidOOM
- Android處理圖片OOM的若干方法小結AndroidOOM
- 關於android 使用bitmap的OOM心得和解決方案AndroidOOM
- 何必冥思苦想,直播app開發中延遲的“罪魁禍首”在這APP
- 訓練深度神經網路失敗的罪魁禍首不是梯度消失,而是退化神經網路梯度
- Linux OOM 機制LinuxOOM
- tikv oom排查過程OOM
- hive job oom問題HiveOOM
- OOM的起點到終點OOM
- [jvm]常見的oom異常JVMOOM
- Linux 的 OOM 終結者LinuxOOM
- 圖片三級快取及OOM--android快取OOMAndroid
- OOM分析之問題一)OOM
- Trino Master OOM 排查記錄ASTOOM
- 使用JVisualVM分析OOMLVMOOM
- 深入理解JVM之OOMJVMOOM
- 如何防止 Elasticsearch 服務 OOM ?ElasticsearchOOM
- IgniteFAQ-9-DataRegion OOMOOM
- Android高效載入大圖、多圖解決方案,有效避免程式OOMAndroid圖解OOM
- java out of memoryJava