您的位置:首页 > 移动开发 > Android开发

Android 内存泄漏工具使用

2015-11-25 20:06 330 查看
首先总结一下,平时编码过程需要注意的事项,避免OOM

我之前也有一篇文章介绍过:

Android内存溢出 内存泄漏

其它很详细的介绍文章也可以参考:

1. Android内存优化之OOM

2. Android应用开发性能优化完全分析

时刻记得不要加载过大的Bitmap对象;譬如对于类似图片加载我们要通过BitmapFactory.Options设置图片的一些采样比率和复用等,具体做法点我参考官方文档,不过过我们一般都用fresco或Glide开源库进行加载。

优化界面交互过程中频繁的内存使用;譬如在列表等操作中只加载可见区域的Bitmap、滑动时不加载、停止滑动后再开始加载。

有些地方避免使用强引用,替换为弱引用等操作。

对批量加载等操作进行缓存设计,譬如列表图片显示,Adapter的convertView缓存等。

尽可能的复用资源;譬如系统本身有很多字符串、颜色、图片、动画、样式以及简单布局等资源可供我们直接使用,我们自己也要尽量复用style等资源达到节约内存。

对于有缓存等存在的应用尽量实现onLowMemory()和onTrimMemory()方法。

尽量使用线程池替代多线程操作,这样可以节约内存及CPU占用率。

尽量管理好自己的Service、Thread等后台的生命周期,不要浪费内存占用。

尽可能的不要使用依赖注入,中看不中用。

尽量在做一些大内存分配等可疑内存操作时进行try catch操作,避免不必要的应用闪退。

尽量的优化自己的代码,减少冗余,进行编译打包等优化对齐处理,避免类加载时浪费内存。

关闭资源对象,比如数据库操作中得Cursor,IO操作的对象

调用了registerReceiver注册广播后,调用unregisterReceiver()来取消

调用了View.getViewTreeObserver().addOnXXXListener, 调用View.getViewTreeObserver().removeXXXListener

随时注意Context的使用是否会导致内存泄漏

现在开始看看,怎么来分析内存泄漏

LeakCanary

使用

参考《LeakCanary 中文使用说明》

在 build.gradle 中加入引用,不同的编译使用不同的引用:

dependencies {
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3'
}


在 Application 中:

public class ExampleApplication extends Application {

@Override public void onCreate() {
super.onCreate();
LeakCanary.install(this);
}
}


这样,就万事俱备了! 在 debug build 中,如果检测到某个 activity 有内存泄露,LeakCanary 就是自动地显示一个通知。

工作机制

RefWatcher.watch() 创建一个 KeyedWeakReference 到要被监控的对象。

然后在后台线程检查引用是否被清除,如果没有,调用GC。

如果引用还是未被清除,把 heap 内存 dump 到 APP 对应的文件系统中的一个 .hprof 文件中。

在另外一个进程中的 HeapAnalyzerService 有一个 HeapAnalyzer 使用HAHA 解析这个文件。

得益于唯一的 reference key, HeapAnalyzer 找到 KeyedWeakReference,定位内存泄露。

HeapAnalyzer 计算 到 GC roots 的最短强引用路径,并确定是否是泄露。如果是的话,建立导致泄露的引用链。

引用链传递到 APP 进程中的 DisplayLeakService, 并以通知的形式展示出来。

demo

一个非常简单的 LeakCanary demo: https://github.com/liaohuqiu/leakcanary-demo 开始使用

public class TestActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
TextView textView = (TextView) findViewById(R.id.test_text_view);

TestDataModel.getInstance().setRetainedTextView(textView);
}

@Override
public void onDestroy() {
super.onDestroy();
}
}
public class TestDataModel {

private static TestDataModel sInstance;
private TextView mRetainedTextView;

public static TestDataModel getInstance() {
if (sInstance == null) {
sInstance = new TestDataModel();
}
return sInstance;
}

public void setRetainedTextView(TextView textView) {
mRetainedTextView = textView;
}
}


很明显,sInstance是静态的,然后持有textView,textview当然持有当前context,就是TestActivity了,所以内存泄漏

退出TestActivity之后,然后过个10s左右,然后出现一个通知



此图说明,sInstance引用了mRetainedTextView,mRetainedTextView引用了mContext,其实mContext就是TestActivity,所以最后泄漏了TestActivity这个instance,导致GC的时候不能被回收。

Eclipse MAT

参考:

1. 内存分析工具 MAT 的使用

2. Android 性能优化之使用MAT分析内存泄露问题

注意生成hprof文件之前,需要先手动的GC,然后再分析

GC_CONCURRENT 当你的堆内存快被用完的时候,就会触发这个GC回收

GC_FOR_MALLOC 堆内存已经满了,同时又要试图分配新的内存,所以系统要回收内存

GC_EXTERNAL_ALLOC 在Android3.0 (Honeycomb)以前,释放通过外部内存(比如在2.3以前,产生的Bitmap对象存储在Native Memory中)时产生。Android3.0和更高版本中不再有这种类型的内存分配了。

GC_EXPLICIT 调用System.gc时产生,上图中就是点击Cause GC按钮手动触发垃圾回收器产生的log信息

MAT中

Merge Shortest Paths to GC Roots 可以查看一个对象到RC Roots是否存在引用链相连接, 在JAVA中是通过可达性(Reachability Analysis)来判断对象是否存活,这个算法的基本思想是通过一系列的称谓”GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走得路径称为引用链,当一个对象到GC Roots没有任何引用链相连则该对象被判定为可以被回收的对象,反之不能被回收,我们可以选择 exclude all phantom/weak/soft etc.references(排查虚引用/弱引用/软引用等)因为被虚引用/弱引用/软引用的对象可以直接被GC给回收.

AS中生成hprof文件



然后选择文件,点击右键转换成标准的hprof文件,就可以在MAT中打开了。

在使用Eclipse或者AndroidStudio抓内存之前,一定要手动点击 Initiate GC按钮手动触发GC



第二个按钮就是GC按钮,第三个就是生成AS可查看的hprof文件按钮
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息