在開發Android
應用的過程當中若是須要處理圖片或者大量數據的時候,就有可能會遇到OOM(java.lang.OutOfMemoryError
),通常出現最多的是在建立Bitmap
上,也有多是在內存中處理了大量的數據上。出現OOM應用會直接崩潰,即便沒有出現OOM,內存使用過大的時候應用也會出現卡頓。因此內存的優化在開發Android應用時是一個比較重要的任務。
通常會針對Bitamp
的內存優化有下面幾種方式:javascript
1. 增長進程的內存
2. 使用Bitmap.Config.ALPHA_8(圖片失真)
3. 顯示的調用System.gc()
4. catch Exception
5. 調用bitmap.recycle()
6. 縮小bitmap的大小(若是是讀取的原圖是一個大圖應該先採用這種方式,Bitmap若是是恰好適配屏幕的就不須要縮小了)
7. 使用弱引用和軟引用(google已經不建議使用了,Android的GC效率很是高,只要保證對象沒有被引用便可)複製代碼
可是咱們忽略掉一個問題就是什麼形成了OOM?
通常來講發生OOM崩潰的地方不必定是內存泄露的地方,崩潰的緣由有多是Activity
形成的內存泄露,也多是操做數據庫形成的內存泄露,當內存已經很是接近峯值的時候,這個時候恰巧要建立一個Bitmap
對象就會發生OOM
(Bitmap對象佔用的內存比較大)。
是什麼緣由形成了內存泄露呢?java
咱們知道Android中每一個對象都有本身的生命週期,好比Activity
的生命週期最後會調用onDestroy
方法作銷燬處理,但若是使用Activity
中調用了相似於Toast
這種對象,就會把這個Activity
的引用傳給了Toast
,而Toast
的生命週期不會隨着Activity
的銷燬而銷燬,這樣就形成了Activity
的內存泄露,它會被Toast
對象引用,沒法被銷燬。
常見的內存泄露造成的緣由:android
那如何知道應用的內存有沒有出現泄露呢?git
Heap Dump
:常見的內存監控方式是Heap Dump
,Heap Dump
是一種在Java中比較經常使用的檢測內存的方式:github
簡單來講就是咱們在一個初始狀態A, 在這個時候Dump一次內存,在作了一些操做以後回到狀態A,再Dump一次內存。
對兩次Dunp的內存數據(hprof
)使用分析工具作分析(MAT
),根據分析的結果就能知道是否存在內存泄露,這種方式比較複雜和繁瑣並非特別易用。算法
Moitors
:這是Android SDK
自帶的內存監控工具,Monitors
能檢測到內存的變化,好比內存是增長仍是減小。
打開一個Activity會致使內存增長,關閉一個Activity會致使內存減小,反覆的作這樣的操做,若是每次打開一個Activity再關閉以後增長的內存不會減小就說明這個Activity有可能有內存泄露,再借助log
輔助進行檢測,就能夠發現內存泄露的問題,
這種方式的缺點是並非特別的準確,由於內存的釋放和對象的生命週期有關也和GC
的調度有關。
另外一種方式就是LeakCanary
,LeakCanary
是一個簡單的,方便的內存檢測工具,能夠輕易的發現內存問題,還會生成更加簡單清晰的報告。數據庫
LeakCanary
是一個開源的檢測內存泄露的java庫。項目地址:github.com/square/leak…
LeakCanary實際上就是在本機上自動作了Heap dump
,對生成的hprof
文件進行分析,展現分析的結果。和手工分析Heap Dump
的方式獲得的結果是同樣的。只不過這部分的工做徹底自動化完成了。
下面是一個LeakCanary的結果截圖:緩存
從上圖能夠看到,LeakCanary
能清晰簡單的展現出那裏有內存泄露的問題。那LeakCanary
如何使用呢?ide
在build.gradle
添加依賴:工具
dependencies {
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3.1'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1'
testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1'
}複製代碼
使用LeakCanary
對應用進行檢測它會影響程序的性能,尤爲是在作Heap dump
和分析
操做時,所以須要在依賴裏面指定對應的版本,debug的時候才進行分析,release的時候不能進行分析。debugCompile
可使用檢測版本:
com.squareup.leakcanary:leakcanary-android
releaseCompile
使用no-op模式,即No Operation Performed
就是不會把對應的類庫編譯,指定類庫爲無用的指令:
com.squareup.leakcanary:leakcanary-android-no-op
這樣就能夠指定LeakCanary
爲無用指令,不會在release的時候進行編譯。
在Application
中加入分析Activity的代碼:
public class ExampleApplication extends Application {
@Override public void onCreate() {
super.onCreate();
LeakCanary.install(this);
}
}複製代碼
這樣就能夠檢測全部Activity
的內存泄露了。LeakCanary
內部實現使用了ActivityLifecycleCallbacks
方法監聽全部Activity的生命週期。
除了Activity
會發生內存泄露之外,其餘對象也有可能會出現內存泄露,若是對其餘對象進行檢測呢?
LeakCanary
中提供了RefWatcher
類,能夠用來監控全部的對象。
首先實例化RefWatcher
:
public static RefWatcher sRefWatcher=LeakCanary.install(mContext);複製代碼
對於監控的對象使用:
sRefWatcher.watch(this)複製代碼
通常咱們是在對象銷燬的時候對對象進行監控,好比內部實現的對於Activity的監控的原理以下:
private final ActivityLifecycleCallbacks lifecycleCallbacks = new ActivityLifecycleCallbacks() {
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
public void onActivityStarted(Activity activity) {
}
public void onActivityResumed(Activity activity) {
}
public void onActivityPaused(Activity activity) {
}
public void onActivityStopped(Activity activity) {
}
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
public void onActivityDestroyed(Activity activity) {
ActivityRefWatcher.this.onActivityDestroyed(activity);
}
};複製代碼
只是在onActivityDestroyed
的時候纔對於activity進行監控便可。
檢測到了內存泄露,若是解決呢?
通常狀況內存泄露的緣由都是因爲引用的使用不當形成的,Android GC
可以保證回收循環引用
(若是一個循環引用沒有外部引用時就會被回收),且Android GC
效率很高,固然GC的算法自己也在不停的改進。
通常狀況下只須要儘可能避免錯誤的引用方式帶來的內存泄露問題便可: