問題: 安卓系統常常遇到OOM問題,如何優化和應對?java
Dalvik 主要管理的內存有 Java heap 和 native heap 兩大塊,而對於一個安卓應用來講,因爲手機設備的限制,通常應用使用的RAM不能超過某個設定值,若是你想要分配超過大於該分配值的內存的話,就會報Out Of Memory 錯誤。不一樣產商默認值不太同樣,通常常見的有16M,24M,32M,48M也就是說app的 Java heap + native heap < 默認值。只因此不等因而由於除了這2種咱們主要關心的內存外,還有一些小塊的內存塊用作其餘用途,如用於垃圾回收的markstack,markbits以及livebits。android
致使OOM 有如下幾種狀況:緩存
1 應用中須要加載大對象,例如Bitmapapp
一張在pc機上用的1024*768圖片,若是直接用在手機屏幕這種小屏幕上,不只沒有提升顯示質量,還容易使內存吃緊。假設照片是用ARGB_8888格式,那麼一張1024×768的圖片須要佔用3M的內存, 4-5張就OOM了。bitmap分辨率越高,所佔用的內存就越大,這個是以2爲指數級增加的。優化
解決方案:當咱們須要顯示大的bitmap對象或者較多的bitmap的時候,就須要進行壓縮來防止OOM問題。咱們能夠經過設置BitmapFactory.Optiions的inJustDecodeBounds屬性爲true,這樣的話不會加載圖片到內存中,可是會將圖片的width和height屬性讀取出來,咱們能夠利用這個屬性來對bitmap進行壓縮。Options.inSampleSize 能夠設置壓縮比。spa
ps: 在android 2.3和之前的版本,bitmap對象的像素數據都是分配在native heap中的,因此咱們在調試過程當中這部份內存是在java heap中看不到的,不過在android 3.0以後,bitmap對象就直接分配在java heap上了,這樣便於調試和管理。所以在3.0以後咱們能夠複用bitmap的內存,而沒必要回收它,不過新的bitmap對象要大小和原來的同樣,到了android 4.4以後,就只要高寬不超過原來的就好了線程
此外,java heap 的大小是動態變化的,它像是一個容器,只有水快滿了,在容許的範圍內,它會擴充它的容量。當水常處於較少的狀況下它會縮小其容量. 上面說過android 2.3以前bitmap對象都是在native heap中分配的,若是咱們在生成一個bitmap對象以前,在java heap 分配了一塊較大的內存,即便以後這段內存被回收了,可是咱們的java heap大小不可能一會兒就隨着這塊內存的回收而縮小,它須要一個過程。而咱們又要在native上分配一個bitmap對象的內存,若是這時候 默認值-java heap(未縮小) = native heap的值不足以分配給這個bitmap對象,那麼就會報OOM錯誤。調試
2 持有無用的對象使其沒法被gc,致使Memory Leak . 也就是咱們說的內存泄漏。致使內存泄漏我瞭解的有如下幾個方面。code
2.1 靜態變量致使的Memory leak對象
靜態變量的生命週期和類是息息相關的,它們分配在方法區上,垃圾回收通常不會回收這一塊的內存。因此咱們在代碼中用到靜態對象,在不用的時候若是不賦null值,消除對象的引用的話,那麼這些對象是很難被垃圾回收的,若是這些對象一多或者比較大的話,程序出現OOM的機率就比較大了。由於靜態變量而出現內存泄漏是很常見的。
2.2 不合理使用Context 致使的Memory leak
android 中不少地方都要用到context,連基本的Activty 和 Service都是從Context派生出來的,咱們利用Context主要用來加載資源或者初始化組件,在Activity中有些地方須要用到Context的時候,咱們常常會把context給傳遞過去了,將context傳遞出去就有可能延長了context的生命週期,最終致使了內存泄漏。例如 咱們將activty context對象傳遞給一個後臺線程去執行某些操做,若是在這個過程當中由於屏幕旋轉而致使activity重建,那麼原先的activity對象不會被回收,由於它還被後臺線程引用着,若是這個activity消耗了比較多的內存,那麼新建activity或者後續操做可能由於舊的activity沒有被回收而致使內存泄漏。因此,遇到須要用到context的時候,咱們要合理選擇不一樣的context,對於android應用來講還有一個單例的Application Context對象,該對象生命週期和應用的生命週期是綁定的。選擇context應該考慮到它的生命週期,若是使用該context的組件的生命週期超過該context對象,那麼咱們就要考慮是否能夠用application context。若是真的須要用到該context對象,能夠考慮用弱引用來WeakReference來避免內存泄漏。
2.3 非靜態內部類致使的Memory leak
非靜態的內部類會持有外部類的一個引用,因此和前面context說到的同樣,若是該內部類生命週期超過外部類的生命週期,就可能引發內存泄露了,如AsyncTask和Handler。由於在Activity中咱們可能會用到匿名內部類,因此要當心管理其生命週期。 若是明確生命週期較外部類長的話,那麼應該使用靜態內部類。
2.4 Drawable對象的回調隱含的Memory leak
當咱們爲某一個view設置背景的時候,view會在drawable對象上註冊一個回調,因此drawable對象就擁有了該view的引用了,進而對整個context都有了間接的引用了,若是該drawable對象沒有管理好,例如設置爲靜態,那麼就會致使Memory leak
在手機版QQ中,對於圖片的處理方法是先加載用戶發送的圖片的小圖,只有用戶點擊了纔去加載大圖,這樣即節省了流量也能防止bitmap過大形成OOM 。另外,對於離開屏幕內容的圖片,也要及時的回收,否則聊天記錄一多也就OOM了。 對於回收的圖片,咱們能夠採用軟引用來作緩存, 這樣在內存不吃緊的狀況下就能提升交互性,由於在Java中軟引用在內存吃緊的時候纔會被垃圾回收,比較適合用作cache.另外在Android 3.1版本起,官方還提供了LruCache來進行cache處理。對於不用的Bitmap對象,咱們要及時回收,不然會形成Memory leak ,因此當咱們肯定Bitmap對象不用的時候要及時調用Bitmap.recycle()方法來使它儘早被GC