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的使用是否会导致内存泄漏
现在开始看看,怎么来分析内存泄漏
在 build.gradle 中加入引用,不同的编译使用不同的引用:
在 Application 中:
这样,就万事俱备了! 在 debug build 中,如果检测到某个 activity 有内存泄露,LeakCanary 就是自动地显示一个通知。
然后在后台线程检查引用是否被清除,如果没有,调用GC。
如果引用还是未被清除,把 heap 内存 dump 到 APP 对应的文件系统中的一个 .hprof 文件中。
在另外一个进程中的 HeapAnalyzerService 有一个 HeapAnalyzer 使用HAHA 解析这个文件。
得益于唯一的 reference key, HeapAnalyzer 找到 KeyedWeakReference,定位内存泄露。
HeapAnalyzer 计算 到 GC roots 的最短强引用路径,并确定是否是泄露。如果是的话,建立导致泄露的引用链。
引用链传递到 APP 进程中的 DisplayLeakService, 并以通知的形式展示出来。
很明显,sInstance是静态的,然后持有textView,textview当然持有当前context,就是TestActivity了,所以内存泄漏
退出TestActivity之后,然后过个10s左右,然后出现一个通知
此图说明,sInstance引用了mRetainedTextView,mRetainedTextView引用了mContext,其实mContext就是TestActivity,所以最后泄漏了TestActivity这个instance,导致GC的时候不能被回收。
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文件按钮
我之前也有一篇文章介绍过:
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文件按钮
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories