使用Handler容易产生的内存泄露以及介绍下Java的4种引用

来源:互联网 发布:mac彻底卸载软件数据 编辑:程序博客网 时间:2024/06/05 00:48

转载请注明出处:王亟亟的大牛之路

最近时间都利用的不太好,都是到下午才开始学习或者做事,一上午都吹B或者XXX用掉了。。。不太好,这里督促下自己不要再懒惰,哈哈!!

再安利下我的整合库,方便大家找资源:https://github.com/ddwhan0123/Useful-Open-Source-Android

废话不多说,今天来讲下一个“经常”遇到的一个内存泄露的情况来引出想提的Java的4种引用方式

在举例子之前先将一些基础的知识,不然基础薄弱的同学还会不理解什么的。(当然,我会讲的很粗略,但是你一定能看懂)

通常Java分配内存有三种策略,分别为 静态存储区

静态存储区: 平时那些static的变量啊,方法啊都在这里面,并且在程序整个运行期间都存在。

:我们执行的那些方法所产生的对象都在这里面,方法结束这些东西就会被释放掉。

:我们平时 new出来的那些对象都会在这里面。

那如何区分堆,栈呢?

局部变量的基本数据类型和引用存储于栈中,引用的对象实体存储于堆中。—— 因为它们属于方法中的变量,生命周期随方法而结束。

成员变量全部存储于堆中(包括基本数据类型,引用和引用的对象实体)—— 因为它们属于类,类对象终究是要被new出来使用的。

那接下来我们来看下例子


Handler 造成的内存泄漏

为什么会出现?

Handler 属于 TLS(Thread Local Storage) 变量, 生命周期和 Activity 是不一致的。所以很有可能我们的Activity的逻辑早就结束了,但是Handler持有Activity的引用导致Activity的资源无法回收。

可以看下这个简单的例子:

public class WjjActivity extends Activity {private final Handler myHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {  // 你的业务行为}}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// 强行延迟操作myHandler.postDelayed(new Runnable() {  @Override  public void run() { /* ... */ }}, 1000 * 60 * 20);finish();}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

myHandler 将其 push 进了消息队列 MessageQueue 里。 
当该 Activity 被 finish() 掉时,延迟执行任务的 Message 还会继续存在于主线程中,它持有该 Activity 的 Handler 引用,所以此时 finish() 掉的 Activity 就不会被回收了 
从而造成内存泄漏(因 Handler 为非静态内部类,它会持有外部类的引用)。

那我们可以在Handler申明的时候加 static,让他存活期跟 Activity 的生命周期就无关了。

然后我又找到了一个更好的解决方法,就是通过弱引用的方式引入 Activity,避免直接将 Activity 作为 context 传进去。

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<SampleActivity> mActivity;    public MyHandler(SampleActivity activity) {      mActivity = new WeakReference<SampleActivity>(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();  }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

这样如果Activity已经不在了,也就不做这些事了。


上面已经用到了弱饮用,那么和强引用又有什么区别呢?就是接下来要讲的。

java的引用有4种分别是 
强引用(StrongReference)软引用(SoftReference)弱引用(WeakReference)虚引用(PhantomReference)

强引用(StrongReference)

强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。 
就是我们平时 new 的那些对象(这里不包括局部变量,别误解)

像这样

Apple a=new Apple();
  • 1
  • 1

如果是方法内的局部变量,在方法结束前你也不需要做把他设置为null的操作,让GC来消除他。显式地设置a为null,或超出对象的生命周期范围,则gc认为该对象不存在引用,这时就可以回收这个对象

像list之类的因为 你通常是 List list =new ArrayList<>();出来了的,所以如果不用了,建议手动clear()下,而不是以为list.get(x)为null了他就会被回收。

软引用(SoftReference)

如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。

String v=new String("wjj");          // 强引用  SoftReference<String> softRef=new SoftReference<String>(v); // 软引用    
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

那么在内存吃紧的时候softRef就会被回收。

使用场景: 
我有一个Activity,他有很多图片,图片都在map里缓存,我切出去再back回来如果不用缓存,每次IO操作去读sd卡,但是有了缓存我就可以迅速的加载,提升用户的体验。如果内存吃紧被回收了,那也只能去重新加载了(强烈抗议刻意不让系统回收这些缓存数据)

弱引用(WeakReference)

在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。

和软引用的区别就是,一个内存不足才回收,一个看到就回收!

用法跟上面几乎一样,就不贴代码了。

应用推荐: 如果这个对象是偶尔的使用,并且希望在使用时随时就能获取到,但又不想影响此对象的垃圾收集,那么你应该用 Weak Reference 来记住此对象。

虚引用(PhantomReference)

如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。

大致的概念如下

这里写图片描述

因为一些个人原因,后面几天我都会处于休假状态,整理的东西会停更几天,恢复工作状态后我会补上吧,提前预祝大家 5.1愉快!

这里写图片描述

部分知识源于网上,Tx for 箫鉴哥

0 0
原创粉丝点击