五分钟搞明白JAVA的软引用,弱引用,虚引用

 

       对于大部分对象而言,程序里会有一个引用变量引用该对象,例如 Object o = new Object();这种引用方式就是常见的引用方式,强引用。除此之外,java.lang.ref包下提供了三个类: SoftReference,WeakReference和PhantomReference,它们分别代表系统对对象的三种引用方式:软引用,弱引用和虚引用。因此,Java对对象的引用方式有如下四种。

  • 强引用(StrongReference)

       这是Java程序中最常见的引用方式,程序创建一个对象,并把这个对象赋给一个引用变量。程序通过该引用变量来操作实际的对象。当一个对象被一个或一个以上的引用变量所引用的时候,它就处于激活状态,不可能被垃圾回收机制回收。当一个对象没有被任何一个引用变量所引用的时候,该对象就会标记为垃圾对象,被垃圾回收机制回收掉。

  • 软引用(SoftReference)

       软引用需要通过SoftReference类来实现。当一个对象只具有软引用的时候,在堆内存充裕的时候,是不会被垃圾回收机制回收的。只有堆内存不足的时候,垃圾回收机制将会回收它。代码如下:

运行的时候记得加一下JVM启动参数:-Xmx10M 调小堆内存。

public class SoftReferenceTest {
    static volatile boolean runFlag = true;
    public static void main(String[] args) throws InterruptedException {
        //new 一个String对象
        String str = new String("Hello,everyone");//①
        //创建一个软引用指向到字符串"Hello,everyony"
        SoftReference<String> sr = new SoftReference<>(str);//②
        //切断str 与 "Hello,everyony" 的关联
        str = null;//③

        //启动一个线程,不停的消耗内存
        Thread t = new Thread(new Runnable() {
            List<byte[]> eatMemList = new LinkedList<>();
            @Override
            public void run() {
                while (true){
                    try {
                        TimeUnit.SECONDS.sleep(1);
                        eatMemList.add(new byte[1024 * 1024]);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }catch (OutOfMemoryError error){
                        System.out.println("内存不足了..");
                        runFlag = false;
                        break;
                    }
                }
            }
        });
        t.start();

        while (runFlag){
            //强制执行GC
            System.gc();
            System.runFinalization();

            //通过软引用获取字符串,null 表示已经别被回收
            String s = sr.get();//④
            if(null == s){
                System.out.println("字符串被垃圾回收器回收掉了");
            }else {
                System.out.println(s);
            }
        }
    }
}

运行结果如下:

 

      可以看到,当内存不足后,软引用引用的对象别垃圾收集器回收掉了。大概步骤如下:

                 在①处创建字符串对象指向str。

                 在②处创建软引用对象sr引用str。

                 在③断开str与字符串"Hello,everyone"的强引用。

                 在④通过软引用对象获取字符串"Hello,everyone"。

        系统内存如下图所示。

  • 弱引用

         弱引用通过WeakReference类实现。弱引用和软引用很像,但是弱引用的引用级别更低。对于只有弱引用引用的对象,当系统进行垃圾回收的时候,不管系统内存是否足够,总会回收该对象。当然,并不是说一个对象只有弱引用的时候,它就会立即被回收,正如那些失去引用的对象一样,必须等到系统垃圾回收器运行的时候才会被回收掉。

public class WeakReferenceTest {
    public static void main(String[] args) throws InterruptedException {
        //new 一个String对象
        String str = new String("Hello,everyone");//①
        //创建一个弱引用指向到字符串"Hello,everyony"
        WeakReference<String> wr = new WeakReference<>(str);//②
        //切断str 与 "Hello,everyony" 的关联
        str = null;//③

        System.out.println("GC前:" + wr.get());
        //强制执行GC
        System.gc();
        System.runFinalization();
        //因为GC的不确定性,所以GC后睡眠一会
        TimeUnit.SECONDS.sleep(5);
        System.out.println("GC前:" + wr.get());
    }
}

运行结果如下:

可以看到GC后弱引用引用的对象会回收掉了。

  • 虚引用

       虚引用通过PhantomReference类实现,虚引用完全类似于完全没有引用。虚引用对对象本身没有太大的影响,对象甚至感觉不到虚引用的存在。如果一个对象只有一个虚引用时,那它和没有引用的效果大致相同。虚引用主要用于跟踪一个对象被垃圾回收的状态,虚引用一般不能单独使用,一般配合引用队列(ReferenceQueue)联合使用。虚引用无法获取它所引用的对象

Demo 如下:

public class PhantomReferenceTest {
    public static void main(String[] args) throws InterruptedException {
        //new 一个String对象
        String str = new String("Hello,everyone");
        //创建一个引用队列
        ReferenceQueue<String> rq = new ReferenceQueue<>();
        //创建一个虚引用指向到字符串"Hello,everyony"
        PhantomReference pr = new PhantomReference(str, rq);
        
        //不能通过虚引用访问所引用对象,所以会打印null
        System.out.println("通过虚引用获取对象为:" + pr.get());//①

        //切断str 与 "Hello,everyony" 的关联
        str = null;
        
        //强制执行GC
        System.gc();
        System.runFinalization();
        //因为GC的不确定性,所以GC后睡眠一会
        TimeUnit.SECONDS.sleep(5);

        //取出引用队列中最先放入队列中的PhantomReference引用与pr对比
        Reference<? extends String> poll = rq.poll();
        System.out.println("" + (poll == pr));//②
    }
}

       运行结果如下:

       因为无法通过虚引用来获取被引用的对象,所以①处代码的输出null(即使此时为字符串对象还为被回收,仍有强引用str指向该字符串对象)。当字符串对象"Hello,everyone"被回收后,与该对象关联的虚引用对象pr将被添加到引用队列rq中,因此将在②代码处看到输出true。

 

最后,不管是软引用,弱引用,还是虚引用当有强引用指向对象的的时候,该对象都不会被垃圾回收器回收掉。

参考:《疯狂JAVA讲义》

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值