Android性能优化(二)——内存泄漏
2017-12-29 17:27
288 查看
Android性能优化(二)——内存泄漏
前言想要彻底搞懂内存泄漏(Memory Leak),就要从三个方面下手,什么是内存泄漏,内存泄漏有什么影响,如何解决内存泄漏三个方面入手。
什么是内存泄漏及影响 ?
每一个对象都是有生命周期的,当生命周期结束的时候会回收的,但是因为其他的持有这个对象的引用导致不能被回收,所以无法释放内存,长此以往的堆积在堆内存中会造成内存泄漏,因为程序分配的内存不足以支持程序运行所需要的内存,最终会导致程序内存溢出(OOM),程序Crash崩溃掉,这对程序开发者和用户来说都是致命的问题。
怎么检测内存泄漏 ?
内存检测工具有很多,这里我推荐两款工具,一个是MAT,一个是Leak Canary三方检测工具,这两个都很好使用,工具没有最好的,只有最适合自己的。这篇博客我使用的是Leak Canary,具体的使用方法这有链接Github-LeakCanary的使用方法
如何解决内存泄漏 ?
这里我写了一张图,供大家欣赏,后面会详细讲解其中的一些常见内存泄漏情况。
第一种常见的内存泄漏:非静态内部类创建静态实例导致的内存泄漏
因为非静态内部类会持有外部类的引用,而使用该非静态内部类又创建了一个静态实例,因为静态实例的生命周期和应用的生命周期一样长,导致该实例一直持有Activity的引用,导致Activity的资源不能被正常回收利用,导致内存泄漏。从下面图片可以看出内存泄漏和当前类中的demo有关,便于进一步查看原因。
解决方法:可以将类写为静态内部类或者将类设置成单例模式即可。
public class StaticOOM1Activity extends AppCompatActivity { static Demo demo; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_static_oom1); RefWatcher refWatcher = App.getRefWatcher(this); refWatcher.watch(this); if (demo == null) { demo = new Demo(); } finish(); } class Demo { } }
还有一种情况如下:因为mContext是静态变量生命周期比较长,一般来说和进程一致,那么此时将mContext=this的代码就将静态变量持有了Activity的引用,导致Activity资源无法回收,内存泄漏,一般不会出现这种情况,因为太明显了。
public class StaticOOM2Activity extends AppCompatActivity { private static Context mContext; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_static_oom1); RefWatcher refWatcher = App.getRefWatcher(this); refWatcher.watch(this); mContext = this; finish(); } }
第二种常见的内存泄漏:单例模式引发的内存泄漏
因为此时单例模式的形参是一个context,如果在Activity中传入this参数的话,那么就会出现内存泄漏,因为单例模式的生命周期和Application一致,当Activity销毁的时候因为单例模式的实例还持有Activity的引用,所以导致Activity无法回收,导致内存泄漏。
public class Person { private static Person person = null; Context mContext; private Person(Context context) { this.mContext = context; } public static Person getInstance(Context context) { if (person == null) { synchronized (Person.class) { if (person == null) { person = new Person(context); } } } return person; } }
解决方法:使用使用context.getApplicationContext()/App.get()这里获取的是整个程序的上下文
public class Person { private static Person person = null; Context mContext; private Person(Context context) { this.mContext = context; } public static Person getInstance(Context context) { if (person == null) { synchronized (Person.class) { if (person == null) { person = new Person(context.getApplicationContext()); // person = new Person(App.get()); } } } return person; } }
第三种常见的内存泄漏:属性动画导致的内存泄漏
因为在Activity中,动画会持有View的引用,而View又会持有Activity的引用,即使Activity关闭了,但是动画还是会继续运行的,不过我们看不见罢了,这样会造成内存泄漏。
public class AnimationOOMActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_static_oom1); RefWatcher refWatcher = App.getRefWatcher(this); refWatcher.watch(this); Button button= (Button) findViewById(R.id.tv); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { finish(); } }); AnimatorSet set = new AnimatorSet(); ObjectAnimator alpha = ObjectAnimator.ofFloat(button, "alpha", 1.0f, 0.5 cea6 f); alpha.setRepeatCount(1000); alpha.setDuration(500); set.playTogether(alpha); set.start(); } }
解决办法:在onDestory()方法中取消动画即可animation.cancel()。
第四种常见的内存泄漏:Handle引起的内存泄漏。
假如我们发送一个延迟一分钟的message的话,Handler的消息处理机制相信大家都已经很熟悉了,通过loop()方法不停的循环遍历消息队列中的消息,因为loop()方法是一个阻塞方法,而且是以队列的形式处理消息的,那么在这一分钟内MessageQueue就会持有message和handler的引用,而handler又是一个非静态内部类,它会持有外部类的引用,这样的话当我们的Activity销毁的时候,就会造成内存泄漏了。
public class HandleOOMActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_static_oom1); RefWatcher refWatcher = App.getRefWatcher(this); refWatcher.watch(this); mHandler.sendEmptyMessageDelayed(1, 60 * 1000); finish(); } @SuppressLint("HandlerLeak") private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (msg.what == 1) { Toast.makeText(HandleOOMActivity.this, "收到了信息", Toast.LENGTH_SHORT).show(); } } };
解决方法:将Handler声明为静态内部类,这样的话就不会持有外部类的引用,就和Activity无关了,就不会造成内存泄漏了,虽然这时候不会造成内存泄漏了,但是我们知道还是有消息在MessageQueue中,Looper也在等待处理消息,所以我们要在Activity结束的时候处理掉队列中的消息。如果用到Context等外部类非static对象时候,需要使用和应用同生命周期的Context为好。
public class HandleOOMActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_static_oom1); RefWatcher refWatcher = App.getRefWatcher(this); refWatcher.watch(this); MyHandler myHandler = new MyHandler(this); myHandler.sendEmptyMessageDelayed(1,60*1000); finish(); } @SuppressLint("HandlerLeak") private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (msg.what == 1) { Toast.makeText(HandleOOMActivity.this, "收到了信息", Toast.LENGTH_SHORT).show(); } } }; private static final class MyHandler extends Handler { private WeakReference<HandleOOMActivity> mActivity; public MyHandler(HandleOOMActivity mainActivity) { //mActivity=mainActivity.getApplicationContext; mActivity = new WeakReference<>(mainActivity); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); HandleOOMActivity mainActivity = mActivity.get(); if (null != mActivity) { //相关处理 } } } /** * 虽然我们结束了Activity的内存泄漏问题,但是经过Handler发送的延时消息还在MessageQueue中, * Looper也在等待处理消息,所以我们要在Activity销毁的时候处理掉队列中的消息。 */ @Override protected void onDestroy() { super.onDestroy(); mHandler.removeCallbacksAndMessages(null); } }
结尾:如上面思维导图所写的那样,其实还有很多会导致内存泄漏的原因,比如I/O流的问题,Bitmap的资源释放,注册和反注册以及不要多次重复的创建对象等等,只要在开发中多多总结,多留心就会有所长进的,本博客也是性能优化的一个重要方面,程序猿们都加油吧…
相关文章推荐
- Android性能优化-内存泄漏
- Android性能优化之常见的内存泄漏
- Android性能优化之常见的内存泄漏
- Android性能优化之常见的内存泄漏
- [Android分享] Android性能优化之常见的内存泄漏
- Android性能优化之常见的内存泄漏
- android 流行框架之性能优化----LeakCanary(内存泄漏检测)
- Android性能优化之内存泄漏
- Android性能优化之常见的内存泄漏
- Android性能优化之常见的内存泄漏
- Android进阶——性能优化——内存泄漏检测——eclipse使用 leakcanary AS使用leakcanary
- Android 应用性能优化-Handler引起的内存泄漏
- Android性能优化之常见的内存泄漏
- Android性能优化之避免内存泄漏的建议
- Android性能优化-内存泄漏的8个Case
- Android性能优化之常见的内存泄漏
- Android性能优化——内存泄漏优化
- Android性能优化——常见的内存泄漏及处理方法
- Android性能优化之常见的内存泄漏
- Android性能优化之常见的内存泄漏