LeakCanary直面项目中的内存泄露
来源:互联网 发布:蜂窝移动数据开启无效 编辑:程序博客网 时间:2024/06/07 09:30
转载请标明出处:http://blog.csdn.net/donkor_/article/details/54095110
前言:
LeakCanary一个直白的展示Android中内存泄露的工具。它是Square公司开源出来的内存泄露自动探测神器,能够在程序发生内存泄漏的时候在通知栏提示通知,而且学习成本巨低。通过学习本文,了解和如何使用LeakCanary工具,同时了解和解决实际开发中出现的经常遇到的内存泄露案例。
更多详细介绍请参见Github地址:https://github.com/square/leakcanary
好了,在学习如何使用LeakCanary之前,我们先对内存泄露与内存溢出做出概念性的理解。原因是大部分人对这两个的区别总是朦朦胧胧分不清楚。
▲概念要点(什么是内存泄露,内存溢出)
- 内存泄露(Memory Leak)指你用malloc或new申请了一块内存,但是没有通过free或delete将内存释放,导致这块内存一直处于占用状态内存泄露和硬件没有关系,它是由软件设计缺陷引起的。
- 内存溢出(Memory Overflow)指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory。比如你申请了10个字节的空间,但是你在这个空间写入11或以上字节的数据,就是溢出
▲内存泄露、溢出的异同(两者之间的区别)
相同点:都会导致应用程序运行出现问题,性能下降或挂起。
不同点:
1) 内存泄露是导致内存溢出的原因之一;内存泄露积累起来将导致内存溢出。
2)内存泄露可以通过完善代码来避免;内存溢出可以通过调整配置来减少发生频率,但无法彻底避免。
▲Android中会造成内存泄露的情景无外乎两种
- 全局进程(process-global)的static变量。这个无视应用的状态,持有Activity的强引用的怪物。
- 活在Activity生命周期之外的线程。没有清空对Activity的强引用。
了解了内存溢出与内存泄露之后,我们接下来看看如何使用LeakCanary工具减少我们项目中的内存泄露的问题。
▲Android Studio集成LeakCanary
在app的build文件加上:
dependencies { debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5' releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5' testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'}
新建MyApplication 中初始化,同时别忘了在AndroidManifest中配置Application标签中的name
public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); if (LeakCanary.isInAnalyzerProcess(this)) { // This process is dedicated to LeakCanary for heap analysis. // You should not init your app in this process. return; } enabledStrictMode(); LeakCanary.install(this); } private void enabledStrictMode() { if (SDK_INT >= GINGERBREAD) { StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() // .detectAll() // .penaltyLog() // .penaltyDeath() // .build()); } }}
LeakCanary集成完成。接下来通过介绍三个经常会在项目中写错的内存泄露实例(很大众的实例!!!),介绍和使用如何LeakCanary。同时给出解决方法!!! 没错,不是五个,不是六个,只有三个。三个不多,但相比百度上搜索常见的内存泄露,写出了5个同时给出文!字!描!述!的解决方法,却不给demo,那才是在耍流氓。但在给出三种解决方法之前,常见的内存泄露情况,我们还是有必要过目一下。
▲常见的内存泄露
- 持有Context引用造成的泄漏
- 线程之间通过Handler通信引起的内存泄漏
- 将变量的作用域设置为最小
- 构造Adapter时,没有使用缓存的convertView
- 资源对象没关闭造成的内存泄露(Cursor、IO 流)
- 各种注册没取消
- 集合容器对象没清理造成的内存泄露
- static关键字的滥用
- WebView对象没有销毁
- GridView的滥用
- Handler的使用
- 线程的使用
- Bitmap的回收和置空(对象内存过大)
(如有纰漏,还望指正)
▲接下来是很大众的内存泄露实例与解决方法
1 . 单例模式造成的内存泄露
//X错误的示范public class InsUtil { private static InsUtil instance; private Context mContext; private InsUtil(Context context) { this.mContext = context; }}
//X错误的示范public class InsUtil { private static InsUtil instance; private Context mContext; private InsUtil(Context context) { this.mContext = context; }}
相信很多人看到上述单例的代码,都会感到内心有一股泥石流,是的,没错,因为自己也是这么写的。而上述造成内存泄露的原因是传入Activity的Context,当前Activity退出时它的内存并不会被回收,因为单例对象持有该Activity的引用。Context的引用超过了本身的生命周期,所以不会被回收。正确的写法是使用Application的Context,使得这个Context的生命周期跟Application一样长
//√正确的示范public class InsUtil { private static InsUtil instance; private Context mContext; private InsUtil(Context context) { this.mContext = context.getApplicationContext(); } public static InsUtil getInsUtil(Context context) { if (instance == null) { instance = new InsUtil(context); } return instance; }}
2 . handler造成的内存泄露
//X错误的示范public class HandlerActivity extends Activity { private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { // do something you want } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mHandler.sendMessageDelayed(Message.obtain(), 10000); //just finish this activity finish(); }}
从上述的HandlerActivity 可以看出,在finish()的时候,该Message还没有被处理,Message持有Handler, Handler持有Activity,这样阻止了GC对Acivity的回收,就发生了内存泄露。正确的写法应该是使用显形的引用,静态内部类与 外部类。使用弱引用WeakReference。 最后在Activity调用onDestroy()的时候要取消掉该Handler对象的Message和Runnable
//√正确的示范public class HandlerActivity extends Activity { private static class MyHandler extends Handler { private final WeakReference<HandlerActivity> mActivityReference; public MyHandler(HandlerActivity activity) { mActivityReference = new WeakReference<>(activity); } @Override public void handleMessage(Message msg) { HandlerActivity handlerAct = mActivityReference.get(); if (handlerAct == null) { return; } // Do something you want } } private MyHandler mHandler ; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mHandler=new MyHandler(HandlerActivity.this); //just finish this activity finish(); } @Override protected void onDestroy() { // Remove all Runnable and Message. mHandler.removeCallbacksAndMessages(null); super.onDestroy(); }}
3 . Thread造成的内存泄露
//X错误的示范public class ThreadActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_thread); //两种常见线程写法造成的内存泄露 new MyAsyncTask().execute(); new Thread(new Runnable() { @Override public void run() { SystemClock.sleep(10000); } }).start(); } private class MyAsyncTask extends AsyncTask<Void,Void,Void>{ @Override protected Void doInBackground(Void... params) { SystemClock.sleep(10000); return null; } }}
从上述ThreadActivity可以看出 以上的异步任务和Runnable都是一个匿名内部类,因此它们对当前Activity都有一个隐式引用。 如果Activity在销毁之前,任务还未完成, 那么将导致Activity的内存资源无法回收,造成内存泄漏。 正确的做法还是使用静态内部类的方式 最后在Activity销毁的时候,相对应的取消异步任务
//√正确的示范public class ThreadActivity extends Activity { private MyAsyncTask myAsyncTask; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_thread); myAsyncTask = new MyAsyncTask(ThreadActivity.this); myAsyncTask.execute(); new Thread(new MyRunnable()).start(); } private static class MyAsyncTask extends AsyncTask<Void, Void, Void> { private WeakReference<Context> weakReference; public MyAsyncTask(Context context) { weakReference = new WeakReference<>(context); } //doInBackground方法内部执行后台任务,不可在此方法内修改UI @Override protected Void doInBackground(Void... params) { SystemClock.sleep(10000); return null; } //onPostExecute方法用于在执行完后台任务后更新UI,显示结果 @Override protected void onPostExecute(Void aVoid) { super.onPostExecute(aVoid); MainActivity activity = (MainActivity) weakReference.get(); if (activity != null) { //... } } //onCancelled方法用于在取消执行中的任务时更改UI @Override protected void onCancelled() { super.onCancelled(); } } static class MyRunnable implements Runnable { @Override public void run() { SystemClock.sleep(10000); } } @Override protected void onDestroy() { //判断异步任务是否存在 if (myAsyncTask != null && myAsyncTask.getStatus() == AsyncTask.Status.RUNNING) { myAsyncTask.cancel(true); } super.onDestroy(); }}
最后下面祭出本案例使用LeakCanary会出现的效果图。
Demo_CSDN 下载地址 : http://download.csdn.net/detail/donkor_/9729808
结尾:
希望这篇文章有帮助到您。欢迎关注我的微信公众号,扫一扫下方二维码,即可关注。有什么问题也可以直接留言,看到之后我会及时回复您。
- LeakCanary直面项目中的内存泄露
- LeakCanary直面项目中的内存泄露
- LeakCanary直面项目中的内存泄露
- LeakCanary直面项目中的内存泄露
- LeakCanary直面项目中的内存泄露
- 使用leakcanary检测Android项目内存泄露
- 使用leakcanary分析程序中的内存泄露
- 内存泄露检测工具LeakCanary
- LeakCanary: 查找内存泄露
- 内存泄露检测工具LeakCanary
- [leakcanary]内存泄露检测
- LeakCanary检查内存泄露
- LeakCanary检查内存泄露
- LeakCanary 检测内存泄露
- 使用开源项目LeakCanary检测应用的内存泄露
- Android 在 Eclipse 项目中使用 Leakcanary 内存泄露检测
- LeakCanary在检测Android项目的内存泄露
- LeakCanary——直白的展现Android中的内存泄露
- win10 永久激活 命令行方式
- iOS UIWebView加载本地HTML页面规避js、css的缓存
- 教你上传本地代码到github
- 百度前产品俞军对PM总结的十二条
- PowerMockito使用详解
- LeakCanary直面项目中的内存泄露
- Linux下配置Jenkins+Android+Ant自动化构建环境(一)
- 【Python学习】 之 Python3.x(小知识点汇集)
- oc消息转发机制
- 1-1 java基础
- 用myeclipse修改文件是出现“some characters cannot be mapped using "iso-8859-1"character encoding
- 【网络流24题】软件补丁问题
- idea hibernate jpa 生成实体类
- winform自定义分页