Java中的引用概念的理解与实践


最初看到了这篇文章十分钟理解Java中的弱引用 ,让我有想法尝试去理解java.lang.ref.WeakReference类的弱引用。首先抱歉对该篇文章会有过多引用的地方。

一。什么是引用

实际上,Java中存在四种引用,它们由强到弱依次是:强引用、软引用、弱引用、虚引用。下面我们简单介绍下除弱引用外的其他三种引用:

强引用(Strong Reference):通常我们通过new来创建一个新对象时返回的引用就是一个强引用,若一个对象通过一系列强引用可到达,它就是强可达的(strongly reachable),那么它就不被回收

软引用(Soft Reference):软引用和弱引用的区别在于,若一个对象是弱引用可达,无论当前内存是否充足它都会被回收,而软引用可达的对象在内存不充足时才会被回收,因此软引用要比弱引用“强”一些

虚引用(Phantom Reference):虚引用是Java中最弱的引用,那么它弱到什么程度呢?它是如此脆弱以至于我们通过虚引用甚至无法获取到被引用的对象,虚引用存在的唯一作用就是当它指向的对象被回收后,虚引用本身会被加入到引用队列中,用作记录它指向的对象已被销毁。

弱引用:

弱引用对象的存在不会阻止它所指向的对象变被垃圾回收器回收。弱引用最常见的用途是实现规范映射(canonicalizing mappings,比如哈希表)。假设垃圾收集器在某个时间点决定一个对象是弱可达的(weakly reachable)(也就是说当前指向它的全都是弱引用),这时垃圾收集器会清除所有指向该对象的弱引用,然后垃圾收集器会把这个弱可达对象标记为可终结(finalizable)的,这样它们随后就会被回收。与此同时或稍后,垃圾收集器会把那些刚清除的弱引用放入创建弱引用对象时所登记到的引用队列(Reference Queue)中。

引用队列:

WeakReference类有两个构造函数:

WeakReference(T referent)//创建一个指向给定对象的弱引用

WeakReference(T referent, ReferenceQueue q)//创建一个指向给定对象并且登记到给定引用队列的弱引用

我们可以看到第二个构造方法中提供了一个ReferenceQueue类型的参数,通过提供这个参数,我们便把创建的弱引用对象注册到了一个引用队列上,这样当它被垃圾回收器清除时,就会把它送入这个引用队列中,我们便可以对这些被清除的弱引用对象进行统一管理。

二。使用弱引用

首先我把文章作者提出的场景拿过来用:

现在有一个Product类代表一种产品,这个类被设计为不可扩展的,而此时我们想要为每个产品增加一个编号。一种解决方案是使用HashMap,于是问题来了,如果我们已经不再需要一个Product对象存在于内存中(比如已经卖出了这件产品),假设指向它的引用为productA,我们这时会给productA赋值为null,然而这时productA过去指向的Product对象并不会被回收,因为它显然还被HashMap引用着。所以这种情况下,我们想要真正的回收一个Product对象,仅仅把它的强引用赋值为null是不够的,还要把相应的条目从HashMap中移除。显然“从HashMap中移除不再需要的条目”这个工作我们不想自己完成,我们希望告诉垃圾收集器:“在只有HashMap中的key在引用着Product对象的情况下,就可以回收相应Product对象了。”

那该如何解决上述问题?我们就会考虑用弱引用概念来解决上述问题,我们会把hashmap的key由product换为WeakReference

本来:

Hashmap hashmap = new Hashmap<>();

Product productA ;

for(int i=0;i<10;i++){

 Product product = new Product();//这句话表明product指向Product对象

if(i=0) productA = product;

hashmap.put(product,i);

}

当把productA = null时,并不能把hashmap对应的product回收。

当改为:

Hashmap hashmap = new Hashmap<>();

WeakReference weakProductA ;

for(int i=0;i<10;i++){

Product product = new Product();

WeakReference weakProduct =new WeakReference<>(product);

if(i=0) weakProductA= weakProduct;

hashmap.put(product,i);

}

现在,弱引用对象weakProductA就指向了Product对象productA。那么我们怎么通过weakProduct获取它所指向的Product对象productA呢?很简单,只需要下面这句代码:

Product product = weakProductA.get();

当我们把product=null时,表明它所引用的Product已经无需存在于内存中,这时指向这个Product对象的就是由弱引用对象weakProductA了,那么显然这时候相应的Product对象时弱可达的,所以指向它的弱引用会被清除,这个Product对象随即会被回收,指向它的弱引用对象会进入引用队列中。

其实对于上述问题,java本身提供了WeakHashMap类,使用和这个类,它的键自然就是弱引用对象,无需我们再手动包装原始对象。

三。使用弱引用

只要某个对象有强引用与之关联,JVM必定不会回收这个对象,即使在内存不足的情况下,JVM宁愿抛出OutOfMemory错误也不会回收这种对象。所以回归到android本身,最容易引起android系统OOM的最大问题就是bitmap问题,因为bitmap本身所占内存就很大,处理不得当,系统就会OOM,发生崩溃。为了保证系统不因bitmap发生OOM,我们考虑用软引用:

Java中的SoftReference即对象的软引用。如果一个对象具有软引用,内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。使用软引用能防止内存泄露,增强程序的健壮性。

下面有一个这样的应用场景:我们通过图片本地路径,可以索引到我们之前通过正常手段获取对应的bitmap。

本来:

Hashmap hashmap = new Hashmap<>();

public Bitmap getBmByPath(String path){

     Bitmap bitmap = hashmap.get(path)

      if(bitmap!=null){

            return bitmap;

      }else{

            bitmap= getBitmapFromPath(path);

            hashmap.put(path,bitmap);

            return bitmap;

     }

}

这样设计导致系统内存不足时,也不会主动回收存储的bitmap。

所以改进为:

Hashmap> hashmap = new Hashmap<>();

public Bitmap getBmByPath(String path){

     SoftReference softbitmap = hashmap.get(path)

     if(softbitmap.get()!=null){

       return softbitmap.get();

    }else{

      bitmap= getBitmapFromPath(path);

      SoftReference d =new SoftReference<>(bitmap);

      hashmap.put(path,d);

      return bitmap;

    }

}

总结:文章上面我写了两个引用,一个是弱引用,一个软应用,为什么不同应用场景我选择不同的引用。我给大家分析一下,在我需要主动回收某对象时,希望它引用的那些对象也会被回收,那我会选择用软引用。当我需要在系统内存不足时,希望系统主动回收某对象时,我会用弱引用。

你可能感兴趣的:(Java中的引用概念的理解与实践)