强、弱、软、虚引用

在Java中,虽然不需要程序员手动去管理对象的生命周期,但是如果希望某些对象具备一定的生命周期的话(比如内存不足时JVM就会自动回收某些对象从而避免OutOfMemory的错误)就需要用到软引用和弱引用了。

从Java SE2开始,就提供了四种类型的引用:强引用、软引用、弱引用和虚引用。Java中提供这四种引用类型主要有两个目的:第一是可以让程序员通过代码的方式决定某些对象的生命周期;第二是有利于JVM进行垃圾回收。

强引用:
强引用就是指在程序代码之中普遍存在的,比如下面这段代码中的object和str都是强引用:
Object object =  new  Object();
String str =  "hello" ;
只要某个对象有强引用与之关联,JVM必定不会回收这个对象,即使在内存不足的情况下,JVM宁愿抛出OutOfMemory错误也不会回收这种对象。 如果想中断强引用和某个对象之间的关联,可以显示地将引用赋值为null。
软引用:

软引用是用来描述一些有用但并不是必需的对象,在Java中用java.lang.ref.SoftReference类来表示。对于软引用关联着的对象,只有在内存不足的时候JVM才会回收该对象。因此,这一点可以很好地用来解决OOM的问题,并且这个特性很适合用来实现缓存:比如网页缓存、图片缓存等。下面是一个使用示例:

public  class  Main {
     public  static  void  main(String[] args) {
         SoftReference<String> str =  new  SoftReference<String>( new  String( "hello" ));
         System.out.println(str.get());
     }
}
软引用可以和一个引用队列(ReferenceQueue)联合使用,具体看后面,如果软引用所引用的对象被JVM回收,这个软引用就会被加入到与之关联的引用队列中。
弱引用:
弱引用也是用来描述非必需对象的,当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象, 需要注意的是, 这里所说的被弱引用关联的对象是指只有弱引用与之关联,如果存在强引用同时与之关联,则进行垃圾回收时也不会回收该对象(软引用也是如此)。在java中,用java.lang.ref.WeakReference类来表示。下面是使用示例:
public  class  Main {
     public  static  void  main(String[] args) {
         WeakReference<String> sr =  new  WeakReference<String>( new  String( "hello" ));
         System.out.println(sr.get());
         System.gc();                 //通知JVM的gc进行垃圾回收
         System.out.println(sr.get()); //输出null,最好等一会再输出,确保垃圾回收站工作
     }
}
弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被JVM回收, 弱引用就会被加入到与之关联的引用队列中。
弱引用一般会与强引用同时使用,当我们需要释放某个对象时,我们只需要释放该对象的强引用,弱引用会在垃圾回收时自动被回收,如果我们在多个地方对某个对象强引用,那么释放该对象时,需要每个地方都释放,如果有一个地方忘了释放都会导致内存泄露,弱引用的好处就是,避免我们手动释放某个对象的所有引用,只需要释放强引用便可。
集合元素是虚引用时,对象被回收后,集合元素并没有被回收,被回收的是元素中的对象,即elem.get()会返回null,但elem不为null,elem需要我们手动删掉,如下面代码:
    public  static  void  main(String[] args)  throws  InterruptedException {
        CopyOnWriteArraySet<WeakReference<String>>set =  new  CopyOnWriteArraySet<WeakReference<String>>();
        String str1 =  new  String( "str1" );
        String str2 =  new  String( "str2" );
        String str3 =  new  String( "str3" );
        set.add( new  WeakReference<String>(str1));
        set.add( new  WeakReference<String>(str2));
        set.add( new  WeakReference<String>(str3));
        for (WeakReference<String>weakReference:set)
            System.out.println(weakReference.get());
        str1 =  null ;
        str2 =  null ;
        System.gc();
        Thread.sleep( 100 );
        System.out.println(set.size());
        for (WeakReference<String>weakReference:set){
            if (weakReference.get()== null )
                set.remove(weakReference);
        }
        System.out.println(set.size());
    }
输出:
str1
str2
str3
3
1
与ReferenceQueue结合使用:
     public  static  void  main(String[] args)  throws  InterruptedException {
         ReferenceQueue<Object> queue =  new  ReferenceQueue<Object>();
         WeakReference<Object>wr =  new  WeakReference<Object>( new  Integer( 10 ),queue);
         System.gc();
         Thread.sleep( 1000 );
         System.out.println(wr);
         System.out.println(queue.poll());
     }
输出:
java.lang.ref.WeakReference@c33f45e
java.lang.ref.WeakReference@c33f45e
对象Integer(10)被回收后,queue收到一个与之对应的弱引用,queue.poll()返回的是一个 WeakReference对象,与wr是同一个对象,但不能通过该引用取得Integer(10)这个对象。
虽然知道对象被回收了,但拿不到被回收的对象,这又有什么用呢?
由于queue收到的对象与wr是同一个对象,所以我们可以继承WeakReference,然后把被回收的对象(也可以包括与之相关的其它属性)传递进去,通过queue.poll()方法取得wr之后,便可知道哪个对象被回收。
例子:
public  class  Test2 {
     public  static  void  main(String[] args)  throws  InterruptedException {
         ReferenceQueue<Object> queue =  new  ReferenceQueue<Object>();
         /*
          * 不能写成new MyWeakReference(new Integer(10),queue);
          * 否则MyWeakReference这个对象本身被回收,这时queue.poll()返回的是null
          */
         MyWeakReference wr =  new  MyWeakReference( new  Integer( 10 ),queue);
         System.gc();
         Thread.sleep( 1000 );
         MyWeakReference mwr = (MyWeakReference)queue.poll();
         if (mwr!= null )
             System.out.println(mwr.getObject());
     }
     private  static  class  MyWeakReference  extends  WeakReference<Object>{
         private  int  num;
         public  MyWeakReference(Integer integer,ReferenceQueue<Object> queue) {
             super (integer,queue);
             this .num = integer.intValue();
         }
         public  int  getObject(){
             return  num;
         }
     }
}
MyWeakReference构造函数参数可随意,但通常至少要包括将要被回收的对象和ReferenceQueue对象,需要的时候可以传入其它参数。
queue是一个队列,可以接收多个MyWeakReference对象,如:
public  class  Test2 {
     public  static  void  main(String[] args)  throws  InterruptedException {
         ReferenceQueue<Object> queue =  new  ReferenceQueue<Object>();
         MyWeakReference wr =  new  MyWeakReference( new  Integer( 10 ),queue);
         MyWeakReference wr1 =  new  MyWeakReference( new  Integer( 11 ),queue);
         MyWeakReference wr2 =  new  MyWeakReference( new  Integer( 12 ),queue);
         System.gc();
         Thread.sleep( 1000 );
         for  (Object x; (x = queue.poll()) !=  null ; ){
             MyWeakReference mwr = (MyWeakReference)x;
             System.out.println(mwr.getObject());
         }
     }
     private  static  class  MyWeakReference  extends  WeakReference<Object>{
         private  int  num;
         public  MyWeakReference(Integer integer,ReferenceQueue<Object> queue) {
             super (integer,queue);
             this .num = integer.intValue();
         }
         public  int  getObject(){
             return  num;
         }
     }
}

虚引用:

虚引用也称为幽灵引用或幻影引用,它是最弱的一种引用关系,虚引用和前面的软引用、弱引用不同,它并不影响对象的生命周期。在java中用java.lang.ref.PhantomReference类表示。如果一个对象与虚引用关联,则跟没有引用与之关联一样,在任何时候都可能被垃圾回收器回收,所以无法通过虚引用来取得一个对象的实例,为一个对象设置虚引用的唯一目的就是希望在这个对象被回收时能收到一个系统通知。

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

public  static  void  main(String[] args)  throws  InterruptedException {
         ReferenceQueue<String> queue =  new  ReferenceQueue<String>();
         PhantomReference<String> pr =  new  PhantomReference<String>( new  String( "hello" ), queue); //pr是hello这个对象的虚引用
         System.gc();
         Thread.sleep( 1000 );
        System.out.println(pr);
         System.out.println(queue.poll());
     }
输出:
java.lang.ref.PhantomReference@2382815a
java.lang.ref.PhantomReference@2382815a
hello这个对象被gc回收了,queue中收到与之对应的虚引用,与pr是同一个对象,但不能通过该引用取得hello这个对象。
与弱引用一样,可以通过继承 PhantomReference来把被回收的对象传递到pr中。










版权声明:本文为博主原创文章,未经博主允许不得转载。

转载于:https://my.oschina.net/u/1987489/blog/491313

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值