内存泄漏:使用弱应用处理外部类引用

public class SampleActivity extends Activity {

  /**
   * Instances of static inner classes do not hold an implicit
   * reference to their outer class.
   */
  private static class MyHandler extends Handler {
    private final WeakReference mActivity;

    public MyHandler(SampleActivity activity) {
      mActivity = new WeakReference(activity);
    }

    @Override
    public void handleMessage(Message msg) {
      SampleActivity activity = mActivity.get();
      if (activity != null) {
        // ...
      }
    }
  }

  private final MyHandler mHandler = new MyHandler(this);

  /**
   * Instances of anonymous classes do not hold an implicit
   * reference to their outer class when they are "static".
   */
  private static final Runnable sRunnable = new Runnable() {
      @Override
      public void run() { /* ... */ }
  };

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // Post a message and delay its execution for 10 minutes.
    mHandler.postDelayed(sRunnable, 1000 * 60 * 10);

    // Go back to the previous Activity.
    finish();
  }
}

这里Handler为什么要用弱引用来持有Activity的引用?和使用软引用有什么区别?

这里使用弱引用持有Activity,就是表示Handler不会占用着对activity的引用而导致系统无法回收,只要系统来回收这个Activity就能回收掉。

在我的理解,使用弱引用而不是软引用,表示Handler将Activity回收权完全地交给组件管理系统:系统什么时候回收Activity我不知道,反正我不占用,只要你GC来回收时就能回收。

如果Activity被回收了,会对Handler业务逻辑有影响吗?这里需要考虑到情形。什么时候GC会来回收Activity?这时应该是用户已经退出或者跳出了这个Activity,而内存紧张,组件管理系统判断需要这个Activity了。

Handler的handleMessage()一般是在异步消息返回后更新数据操作的,这时如果Activity已经隐入后台了,一般也不再需要UI或数据的更新了。当然如果需要这个Activity常驻内存,当用户回到这个Activity可以看到更新后的数据,可以使用其他方法。

强引用

只要某个对象有强引用与之关联,JVM必定不会回收这个对象,即使在内存不足的情况下,JVM宁愿抛出OutOfMemory错误也不会回收这种对象。

如果想中断强引用和某个对象之间的关联,可以显示地将引用赋值为null,这样一来的话,JVM在合适的时间就会回收该对象。

软引用

软引用在Java中用java.lang.ref.SoftReference类来表示。

对于软引用关联着的对象,只有在内存不足的时候JVM才会回收该对象。因此,这一点可以很好地用来解决OOM的问题,并且这个特性很适合用来实现缓存:比如网页缓存、图片缓存等。

软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被JVM回收,这个软引用就会被加入到与之关联的引用队列中。

import java.lang.ref.SoftReference;
 
public class Main {
    public static void main(String[] args) {
         
        SoftReference sr = new SoftReference(new String("hello"));
        System.out.println(sr.get());
    }
}

弱引用

弱引用用java.lang.ref.WeakReference类来表示。

当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。

import java.lang.ref.WeakReference;
 
public class Main {
    public static void main(String[] args) {
     
        WeakReference sr = new WeakReference(new String("hello"));
         
        System.out.println(sr.get());
        System.gc(); //通知JVM的gc进行垃圾回收
        System.out.println(sr.get());
    }
}

输出结果

hello
null

第二个输出结果是null,这说明只要JVM进行垃圾回收,被弱引用关联的对象必定会被回收掉。

虚引用

虚引用在java中用java.lang.ref.PhantomReference类表示。

虚引用并不影响对象的生命周期。如果一个对象与虚引用关联,则跟没有引用与之关联一样,在任何时候都可能被垃圾回收器回收。

要注意的是,虚引用必须和引用队列关联使用,当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会把这个虚引用加入到与之 关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

软引用和弱引用的区别

它们都是用来描述非必需对象的,但是被软引用关联的对象只有在内存不足时才会被回收,而被弱引用关联的对象在JVM进行垃圾回收时总会被回收。

引用类的使用

在SoftReference类中,有三个方法,两个构造方法和一个get方法(WekReference类似):

两个构造方法:

public SoftReference(T referent) {
    super(referent);
    this.timestamp = clock;
}
 
public SoftReference(T referent, ReferenceQueue q) {
    super(referent, q);
    this.timestamp = clock;
}

get方法用来获取与软引用关联的对象的引用,如果该对象被回收了,则返回null。

在使用软引用和弱引用的时候,我们可以显示地通过System.gc()来通知JVM进行垃圾回收。要注意的是,虽然发出了通知,JVM不一定会立刻执行。

你可能感兴趣的:(内存泄漏:使用弱应用处理外部类引用)