Android 最佳性能实践之内存管理(二)
2016-05-29 21:22
429 查看
上一篇文章针对官方文档做了一些翻译整理工作,主要针对是原理层面进行分析,第二篇主要是怎么做。已经做了一小部分翻译,然后想起了guolin郭神的博客,回顾了下发现郭神已经把官方文档翻译完了,而且总结的非常好,不需要再重复造轮子了。这里直接贴上原文链接,感谢郭神贡献。/article/1562107.html
接下来我们使用几个工具分析下最常见的内存释放问题,内存泄漏:
下面的例子是Activity中,一个无法释放内存的例子。
MemoryLeakClass是一个持续工作的线程,List是为了占用更多的内存方便演示内存无法释放问题。我们可以发现,因为Thread在Activity内部,所以Activity的引用无法释放,也就是Activity内存泄漏了。
我演示下mac平台下android studio环境的mat使用方式,其他环境也是大同小异。首先下载mat,链接地址: http://www.eclipse.org/mat/downloads.php 。选择mac平台下的下载后打开。
在android device monitor中选择自己应用的进程,点击update heap,选择heap后点击GC触发GC。
反复进入退出内存泄漏的界面,我们调用GC可以看到内存的情况。
Heap Size 堆的大小,当资源增加,当前堆的空余空间不够时,系统会增加堆的大小,若超过上限 (例如64M,视平台和具体机型而定)则会被杀掉
Allocated 堆中已分配的大小,这是应用程序实际占用的内存大小,资源回收后,此项数据会变小
我们可以发现Allocated不断的增大,也就是内存无法被释放,如果我们的Activity中对象和资源更多,这个问题必定更加严重。
发现了内存泄漏,我们接下来要找到他,就要让mat派上用场了。
在android device monitor中点击导出选择保存位置后,打开命令行,输入命令 hprof-conv memoryleakdemo1.hprof converted-dump.hprof 后面的两个参数是文件的名字。
然后使用mat打开生成的converted-dump.hprof文件。点击创建histogram,然后选择list objects with incoming referenence,正则表达式匹配包名,然后右健 Path to GC Roots exclue all phantom/weak/soft etc. reference,就能看到最终的结果,activity实例自己this没有被释放。
我们往往会习惯于使用activity作为listener,但是在activity销毁的时候没有注销监听。因为单例模式生命周期与application是相同的,所以造成activity被单例所持有,无法释放内存。
静态
如果我们类中的静态变量持有activity或者某些对象,因为静态变量生命周期是与类相同的就会造成内存泄漏。
无限循环的属性动画
这种情况比较容易被忽略,从3.0开始安卓支持属性动画,如果在activity销毁的时候不停止属性动画的循环,那么动画就还会一直循环下去,造成无法释放内存。
然后在清单文件中注册这个Application。这样就可以了,出现内存泄漏后,会弹出通知。我们继续使用上面的例子,进入SecondActivity然后再退出这个Activity。
甚至连泄露了42k都显示出来了,点击查看详情。
Logcat中也能看到日志
这里可以看到内存泄露的类名和具体泄露的详细信息。大大提升了查找内存泄漏问题的速度。在应用中Activity中的内存泄漏现在无处遁行了,如果觉得还不过瘾,我们再看看fragment中的内存泄漏怎么监控。
RefWatcher专门用来监控需要回收的对象。
在application中这样做
fragment中的代码:
有了这个神器,我们再也不会谈内存泄漏色变了。
接下来我们使用几个工具分析下最常见的内存释放问题,内存泄漏:
MAT
MAT是我们常用的内存泄漏分析工具,使用它可以帮助我们找到代码中的内存泄漏,并且修复它。下面的例子是Activity中,一个无法释放内存的例子。
public class SecondActivity extends Activity { private List<String> list = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); for(int i=0;i<2000;i++){ list.add("test"); } MemoryLeakClass memoryLeakClass = new MemoryLeakClass(); memoryLeakClass.start(); } class MemoryLeakClass extends Thread { @Override public void run() { while (true) { Log.i("MemoryLeakClass", "run"); try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
MemoryLeakClass是一个持续工作的线程,List是为了占用更多的内存方便演示内存无法释放问题。我们可以发现,因为Thread在Activity内部,所以Activity的引用无法释放,也就是Activity内存泄漏了。
我演示下mac平台下android studio环境的mat使用方式,其他环境也是大同小异。首先下载mat,链接地址: http://www.eclipse.org/mat/downloads.php 。选择mac平台下的下载后打开。
在android device monitor中选择自己应用的进程,点击update heap,选择heap后点击GC触发GC。
反复进入退出内存泄漏的界面,我们调用GC可以看到内存的情况。
Heap Size 堆的大小,当资源增加,当前堆的空余空间不够时,系统会增加堆的大小,若超过上限 (例如64M,视平台和具体机型而定)则会被杀掉
Allocated 堆中已分配的大小,这是应用程序实际占用的内存大小,资源回收后,此项数据会变小
我们可以发现Allocated不断的增大,也就是内存无法被释放,如果我们的Activity中对象和资源更多,这个问题必定更加严重。
发现了内存泄漏,我们接下来要找到他,就要让mat派上用场了。
在android device monitor中点击导出选择保存位置后,打开命令行,输入命令 hprof-conv memoryleakdemo1.hprof converted-dump.hprof 后面的两个参数是文件的名字。
然后使用mat打开生成的converted-dump.hprof文件。点击创建histogram,然后选择list objects with incoming referenence,正则表达式匹配包名,然后右健 Path to GC Roots exclue all phantom/weak/soft etc. reference,就能看到最终的结果,activity实例自己this没有被释放。
可能引起内存泄漏的代码
单例模式我们往往会习惯于使用activity作为listener,但是在activity销毁的时候没有注销监听。因为单例模式生命周期与application是相同的,所以造成activity被单例所持有,无法释放内存。
静态
如果我们类中的静态变量持有activity或者某些对象,因为静态变量生命周期是与类相同的就会造成内存泄漏。
无限循环的属性动画
这种情况比较容易被忽略,从3.0开始安卓支持属性动画,如果在activity销毁的时候不停止属性动画的循环,那么动画就还会一直循环下去,造成无法释放内存。
LeakCanary
推荐square推出的leakcanary,不得不佩服square总能给开发者们带来福音,有了这个库我们可以很方便的找到应用中的内存泄漏。使用方法超级简单。dependencies { debugCompile 'com.squareup.leakcanary:leakcanary-android:1.4-beta2' releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta2' testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.4-beta2' }
public class ExampleApplication extends Application { @Override public void onCreate() { super.onCreate(); LeakCanary.install(this); } }
然后在清单文件中注册这个Application。这样就可以了,出现内存泄漏后,会弹出通知。我们继续使用上面的例子,进入SecondActivity然后再退出这个Activity。
甚至连泄露了42k都显示出来了,点击查看详情。
Logcat中也能看到日志
这里可以看到内存泄露的类名和具体泄露的详细信息。大大提升了查找内存泄漏问题的速度。在应用中Activity中的内存泄漏现在无处遁行了,如果觉得还不过瘾,我们再看看fragment中的内存泄漏怎么监控。
RefWatcher refWatcher = {...}; // We expect schrodingerCat to be gone soon (or not), let's watch it. refWatcher.watch(schrodingerCat);
RefWatcher专门用来监控需要回收的对象。
在application中这样做
public class ExampleApplication extends Application { public static RefWatcher getRefWatcher(Context context) { ExampleApplication application = (ExampleApplication) context.getApplicationContext(); return application.refWatcher; } private RefWatcher refWatcher; @Override public void onCreate() { super.onCreate(); refWatcher = LeakCanary.install(this); } }
fragment中的代码:
public abstract class BaseFragment extends Fragment { @Override public void onDestroy() { super.onDestroy(); RefWatcher refWatcher = ExampleApplication.getRefWatcher(getActivity()); refWatcher.watch(this); } }
有了这个神器,我们再也不会谈内存泄漏色变了。
相关文章推荐
- 15 个 Android 通用流行框架大全
- android启动画面静态版本实现
- Android 6.0+ 运行时权限探索
- 描述Android系统在视频播放时来电话的解决方案
- Android1.6 启动init.c分析
- Android中的定时任务实现方式
- Android使用百度地图SDK实现定位与方向传感器匹配
- Android中TextView给指定的文字染色
- Android - 开发页面需了解的dip,sp,px知识,以及它们的转换
- Android学习(57) -- xUtils简介使用
- Android SurfaceView实战 打造抽奖转盘
- android shape详解
- Android学习(56) -- 断点续传多线程下载(Android)
- Android学习(55) -- 带断点续传的多线程下载(Java)
- android开发那些事儿(五)-通用流行框架大全
- 教你如何用Circleimageview十秒钟写出万能视频的Android圆形控件适配器
- 从头学android_ListView的使用
- Android Sqlite的基本用法
- 实习入职第九天:android中的seekbar加了图片后,拖动球显示不全问题
- 作业——在线学习Android课程之第十三周(图片优化)