阅读 132

【拒绝一问就懵】之不可忽视的内存泄露

背景介绍

内存泄漏一种情况是一块内存没有引用指向它,却没被回收,这种情况已经由Java虚拟机的 GC

帮助我们处理好了,见 【拒绝一问就懵】之你多少要懂点内存回收机制;另一种属于 逻辑内存泄漏,即一个对象已经不再被使用了,但它仍然被另一个被使用中的对象所持有,导致该对象所占用的内存块不能被回收。

少部分内存泄漏看不出有什么影响,但如果大量发生,将会明显减少可用内存,导致频繁GC,运行缓慢,严重时将容易引发OutOfMemoryError

Android中常见的内存泄漏

集合引发的泄漏

一些对象缓存到集合中,当它不在被使用时,没有从集合中移除,就造成了泄漏。

Activity中的泄漏

一个Activity中的Context通常会被很多地方引用,如果在Activity执行了onDestory()后这些引用没有被置空,将会导致Activity无法被回收。而Activity中依赖了很对对象,这些对象将都不能被回收,所以一定要十分小心Activity的泄漏。以下几种常见的情况需要注意:

  • 当创建一个对象,该对象需要Context作为参数时。当该对象在Activity执行了onDestory()后仍然可到达,那么它持有的Activity就无法被回收。
  • 使用匿名内部类Handler时。Handler会间接的持有外部Activity的引用,当Activity执行了onDestory()后,若Handler中仍然有消息在MessageQueue中,那么该Handler就不能被释放,也就意味着Activity也不会被释放。解决方法:
    • 需要在onDestory()完成前确保Handler没有执行yan shi延时任务,没有被其他地方引用。同时清理Handler中所有任务,调用removeCallbacksAndMessages(null);,该方法参数为空时会移除所以的CallBack和Message。
    • 另一种方法是自定义一个静态内部Handler,让它持有的Activity为弱引用。
    static class MyHandler extends Handler {
          private final WeakReference<Activity> mActivity;
          public MyHandler(Activity activity) {
              mActivity = new WeakReference<Activity>(activity);
          }
          @Override
          public void handleMessage(Message msg) {
              System.out.println(msg);
              if (mActivity.get() == null) {
                  return;
              }
              mActivity.get().todo();
          }
      }
    复制代码
  • 在异步任务中引用Activity或Context,而没有在Activity销毁前停止线程,就会导致Activity的泄漏。
  • 内部类、匿名内部类会持有外部类的实例,所以在Activity中使用内部类或匿名内部类,并且将该类创建的对象传递给其他对象,若该对象在Activity之外被引用,或是在仍在进行的异步任务中,那么Activity将不能释放。

Bitmap引发的内存泄漏

Bitmap是引用中占用内存的大户,在使用时一定要十分注意。当Bitmap不用时,应该及时调用recycle()释放其申请的内存。

异步任务引发的泄漏

异步任务如果在它应该停止的时候没有停止,那它本身及它所依赖的对象将都不能被正确的释放。比如上面提到的Activity中的异步任务。

比如在Activity中这样来开启一个异步任务:

new Thread(()->{
  // doSomething with long-time 
}).start();
复制代码

那么,在Activity已经finish()了,但异步任务中的Runnable还被持有,并且由于是属于Activity的匿名内部类,所以会导致Activity的内存不能及时回收。

总结

由于系统内存资源有限,所以我们需要尽量避免 内存泄漏,以保证程序能够流畅运行。

  • 由于Activity泄漏将会连带大量的对象一起泄漏,所以在Activity中写代码应该特别注意Handler、内部类及匿名内部类,还有Context不能随便传递。
  • 使用异步任务时一定要明确其作用范围,在应该停止的时候记得停止。
  • Bitmap占用内存通常较大,所以在使用完后及时释放十分重要。
  • 此外,在传递参数到单例中时,也需要谨慎。


关注下面的标签,发现更多相似文章
评论