在开发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的算法自己也在不停的改进。
通常状况下只须要尽可能避免错误的引用方式带来的内存泄露问题便可: