内存泄漏缘由以及解决方案

来源:互联网 发布:java中注解的作用 编辑:程序博客网 时间:2024/06/10 19:22

内存泄漏缘由以及解决方案

>
1. 传统的内存泄漏是由忘记释放分配的内存导致的,而逻辑上的内存泄漏则是由于忘记对象不再被使用的时候释放对其的引用导致的
如果一个对象仍存在强引用,GC就无法对其进行回收.
2. 安卓平台,泄漏Context对象问题尤其严重,这是因为像Activity这样的Context对象会引用大量很占内存的对象,如View的层级以及其他资源,
如果Context对象发生的内存泄漏,它所引用的所有对象都被泄漏

对于一个普通的对象,或许我们很难去判定什么时候是内存泄漏,但是Activity这种存在明显的生命周期定义的对象,如果Activity的OnDestroy方法
被调用之后,context还被强引用,那我们可以认为改Activity发生了内存泄漏

>
Activity的泄漏清空一般分配两种
1. 被进程全局的静态变量所持有 (单例,Context赋值给静态变量等)
2. 被比Acitivty生命周期更长的线程所持有 (异步耗时线程等)

场景分析

1. 静态实例持有Activity实例

别说下述代码不可能,其实有很多情况下,我们就这么干,比如你一个静态的方法需要Context,
你可能就把Context置为static了

 private static TestActivity activityReference;    void setStaticActivity() {        activityReference = this;    }

静态变量持有Activity对象很容易造成内存泄漏,因为静态变量是全局存在的,所以当Activity生命周期结束时,引用仍被持有。
我们需要正确的释放引用让垃圾回收机制在它被销毁的同时将其回收。

解决方法:
使用弱引用来代替强引用

 private static WeakReference<TestActivity> activityReference;    void setStaticActivity() {        activityReference = new WeakReference<TestActivity>(this);    }

2. 静态实例持有View实例

看似Activity销毁之后就没什么事,要知道View创建是需要Context,也就是说View持有Context的强引用,
View泄漏,从而导致Context泄漏

public static View mView ;void setStaticView() {  mView = findViewById(R.id.xxx);}

解决方法:
  在OnDestroy()将mView手动置为null

 3. 内部类(匿名内部类)

内部类是很常见的使用方法,因为内部类会隐式持有外部类对象的引用,如果内部类被静态对象持有了,
或者说内部类生命周期较长(例如耗时线程 Thread Timer)

解决方法:
    1. 避免静态变量
2. 使用静态内部类,打破持有链
3.  onDestroy方法中断内部类
4. 弱引用

4. Handler

Handler发送的Runnable持有Context引用,如果Activity销毁的时候,消息还没有处理,
就会导致Context被继续持有得不到释放

 private final Handler mLeakyHandler = new Handler() {    @Override    public void handleMessage(Message msg) {    }  }  @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    mLeakyHandler.postDelayed(new Runnable() {      @Override      public void run() {}    }, 1000 * 60 * 10);  }

当activity结束(finish)时,里面的延时消息在得到处理前,会一直保存在主线程的消息队列里持续10分钟。而且,由上文可知,这条消息持有对handler的引用,而handler又持有对其外部类(在这里,即Activity)的潜在引用。这条引用关系会一直保持直到消息得到处理,从而,这阻止了Activity被垃圾回收器回收,同时造成应用程序的泄漏。

注意,上面代码中的Runnable类–非静态匿名类–同样持有对其外部类的引用。从而也导致泄漏。

  1. 只要有未处理的消息,那么消息会引用handler,非静态的handler又会引用外部类,即Activity,导致Activity无法被回收,造成泄漏;
    Runnable类属于非静态匿名类,同样会引用外部类。

为了解决遇到的问题,我们要明确一点:静态内部类不会持有对外部类的引用。所以,我们可以把handler类放在单独的类文件中,或者使用静态内部类便可以避免泄漏。

  1. 另外,如果想要在handler内部去调用所在的外部类Activity,那么可以在handler内部使用弱引用的方式指向所在Activity,这样统一不会导致内存泄漏。

对于匿名类Runnable,同样可以将其设置为静态类。因为静态的匿名类不会持有对外部类的引用。

 5. Sensor Manager注册监听器导致的内存泄漏

如果 context 对象想要在服务内部的事件发生时被通知,那就需要把自己注册到服务的监听器中。然而,这会让服务持有 activity 的引用,
如果程序员忘记在 activity 销毁时取消注册,那就会导致 activity 泄漏了。

void registerListener() {       SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);       Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ALL);       sensorManager.registerListener(mContext, sensor, SensorManager.SENSOR_DELAY_FASTEST);}

解决方法:
  在onDestroy()方法中注销监听器



  1. 静态内部类

  2. 弱引用

  3. 及时注销之间注册的监听(被监听的动作的全局的,setOnclick()就不必了,页面销毁,View销毁,监听器的对Context持有自然消失了)

原创粉丝点击