zoukankan      html  css  js  c++  java
  • 内存泄漏(Memory Leak)

    什么情况下会导致内存泄露(Memory Leak)?

    Android 的虚拟机是基于寄存器的Dalvik,它的最大堆大小一般是16M,有的机器为24M。因此我们所能利用

    的内存空间是有限的。如果我们的内存占用超过了一定的水平就会出现OutOfMemory 的错误。

    内存溢出的几点原因:


    1、资源释放问题
    程序代码的问题,长期保持某些资源,如Context、Cursor、IO 流的引用,资源得不到释放造成内存泄露。

    需要适当的释放资源的情况,这些硬件资源可能包括:视频、音频、相机等。

    2、广播注册后没取消造成的内存泄漏

    记得在activity销毁的时候一定要取消广播的注册 


    3、static 关键字的使用问题
    static 是Java 中的一个关键字,当用它来修饰成员变量时,那么该变量就属于该类,而不是该类的实例。

    所以用static 修饰的变量,它的生命周期是很长的,如果用它来引用一些资源耗费过多的实例(Context 的情况最多),

    这时就要谨慎对待了。

    public class ClassName {
    private static Context mContext;
    //省略
    }
    以上的代码是很危险的,如果将Activity 赋值到mContext 的话。那么即使该Activity 已经onDestroy,

    但是由于仍有对象保存它的引用,因此该Activity 依然不会被释放。


    我们举Android 文档中的一个例子。

    private static Drawable sBackground;
      @Override
      protected void onCreate(Bundle state) {
      super.onCreate(state);
      TextView label = new TextView(this);
      label.setText("Leaks are bad");
      if (sBackground == null) {
        sBackground = getDrawable(R.drawable.large_bitmap);
      }
      label.setBackgroundDrawable(sBackground);
      setContentView(label);
    }
    sBackground 是一个静态的变量,但是我们发现,我们并没有显式的保存Contex 的引用,但是,当Drawable
    与View 连接之后,Drawable 就将View 设置为一个回调,由于View 中是包含Context 的引用的,所以,实
    际上我们依然保存了Context 的引用。这个引用链如下:Drawable->TextView->Context
    所以,最终该Context 也没有得到释放,发生了内存泄露。
    针对static 的解决方案
    ① 应该尽量避免static 成员变量引用资源耗费过多的实例,比如Context。
    ② 此时的Context 尽量使用ApplicationContext,因为Application 的Context 的生命周期比较长,引用它不会出现内存泄露的问题。
    ③ 使用WeakReference 代替强引用。比如可以使用WeakReference<Context> mContextRef;


    4、线程导致内存溢出
    线程产生内存泄露的主要原因在于线程生命周期的不可控。我们来考虑下面一段代码。

    public class MyActivity extends Activity {
      @Override
      public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.main);
      new MyThread().start();
      }
    private class MyThread extends Thread{
      @Override
      public void run() {
      super.run();
      //do somthing
        }
      }
    }
    这段代码很平常也很简单,是我们经常使用的形式。我们思考一个问题:假设MyThread 的run 函数是一个很费
    时的操作,当我们开启该线程后,将设备的横屏变为了竖屏,一般情况下当屏幕转换时会重新创建Activity,按照我
    们的想法,老的Activity 应该会被销毁才对,然而事实上并非如此。
    由于我们的线程是Activity 的内部类,所以MyThread 中保存了Activity 的一个引用,当MyThread 的run 函
    数没有结束时,MyThread 是不会被销毁的,因此它所引用的老的Activity 也不会被销毁,因此就出现了内存泄露的问题。

    有些人喜欢用Android 提供的AsyncTask,但事实上AsyncTask 的问题更加严重,Thread 只有在run 函数不结
    束时才出现这种内存泄露问题,然而AsyncTask 内部的实现机制是运用了ThreadPoolExcutor,该类产生的Thread 对
    象的生命周期是不确定的,是应用程序无法控制的,因此如果AsyncTask 作为Activity 的内部类,就更容易出现内存泄露的问题。
    针对这种线程导致的内存泄露问题的解决方案:
    ①  将线程的内部类,改为静态内部类(因为非静态内部类拥有外部类对象的强引用,而静态类则不拥有)。
    ②  在线程内部采用弱引用保存Context 引用。

    5、Handler内存泄漏
    Handler作为内部类存在于Activity中,但是Handler生命周期与Activity生命周期往往并不是相同的,比如当Handler对象有Message在排队,则无法释放,进而导致本该释放的Acitivity也没有办法进行回收。 解决办法:

    ① 在onDestroy里移除msg或callback

    ② 声明handler为static类,这样内部类就不再持有外部类的引用了,就不会阻塞Activity的释放

    ③ 如果内部类实在需要用到外部类的对象,可在其内部声明一个弱引用引用外部类。

    6、使用application的context来替代activity

    这是一个很隐晦的内存泄漏的情况。有一种简单的方法来避免context相关的内存泄漏,最显著地一个是避免context逃出他自己的范围之外,使用Application context,这个context的生存周期和你的应用的生存周期一样长,而不是取决于activity的生存周期。如果你想保持一个长期生存的对象,并且这个对象需要一个context,记得使用application对象。

    7、集合中对象没清理造成的内存泄漏

    我们通常把一些对象的引用加入到了集合中,当我们不需要该对象时,并没有把它的引用从集合中清理掉,这样这个集合就会越来越大。

    如果这个集合是static的话,那情况就更严重了。

    附: 

    1. Android内存泄漏的检测流程、捕捉以及分析

  • 相关阅读:
    Spring MVC注解中@PathVariable和@RequestParam的区别
    Spring MVC请求流程
    eclipse-web项目目录结构
    数论基础------质数板
    线性DP基础--acwing---动态规划
    背包基础
    ----------动态规划分界线----------
    2020牛客暑期多校训练营(第三场)
    区间选点-贪心-acwing
    2020牛客暑期多校训练营(第二场)
  • 原文地址:https://www.cnblogs.com/wytiger/p/10407403.html
Copyright © 2011-2022 走看看