强引用 软引用 弱引用 虚引用&ReferenceQueue Reference分析

年前看到一个关于软引用弱引用相关的面试题,自己之前也就听说过弱引用的概念。所以最近把相关ref包下的源码看了下,也找了点资料,把这部分内容总结一下,如下是这篇博客的大纲

一.引入

二.几种引用的概念和使用

三.几种引用的使用场景和注意事项

四.JDK中的ReferenceQueue

五.总结



一.引入

在java中,我们肯定会碰到OOM(OutOfMemory)问题。为什么会出现这个问题,怎么解决呢,今天这个内容就给出了一种方案来说明,解决


二.几种引用的概念和使用

从JDK1.2之前的版本中,若一个对象不被任何变量引用,那么程序就无法再使用这个对象,也就是说,只有对象时可触及状态(reachable),程序

才能使用它,从1.2开始,把对象的引用分为四个级别,由高到低依次是:强引用,软引用,弱引用,虚引用。从而使程序能够更灵活地控制对象

的生命周期(1.可以通过代码的方式决定某些对象的生命周期;2.有利于JVM进行垃圾回收)。关于引用相关的类都在java.lang.ref包下,如下图

所示:



2.1强引用(StrongReference)

强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机

宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。

强引用在日常代码中普遍存在,比如下面代码test方法内部有三个强引用list,str,objArray,这三个引用保存在栈中

而真正的应用内存(new出的对象)保存在堆中。

public void test() {
	List<String> list = new ArrayList<String>();
	String str = "S1amDuncan";
	Object[] abjArray = new Object[10000];
}

只要对象有强引用与之关联,那么JVM就不会回收这个对象,比如第三个objArray,

JVM宁愿抛出OutOfMemoryError也不会回收这个对象。不过需要注意的是当test方法使用完后,list,str,objArray就不存在了,

所以他们指向的对象会被JVM回收。如果想中断强引用和某个对象之间的关联,可以显示的将引用赋值为null,这样,

JVM就会在合适的时间回收该对象,这个在JDK源码中有很多地方应用到了,

我们平时在阅读JDK源码的时候可能会看到如下代码注释,就是这样做的。

// Let gc do its work


2.2软引用(SoftReference)

如果一个对象只具有软引用,如果内存空间足够,垃圾回收器就不会回收它,如果内存不够了,就会回收这些对象的内存,只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可以用来实现内存敏感的高速缓存。软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引起的对象被垃圾回收期回收,Java虚拟机就会把这个软引用加入到与之相关联的引用队列中。使用示例如下

public class TestSoft {
    public static void main(String[] args) {        
        SoftReference<String> sr = new SoftReference<String>(new String("hello"));
        System.out.println(sr.get());
    }
}

2.3弱引用(WeakReference)

弱引用与软引用的区别在于:弱引用的对象(仅持有弱引用)拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,无论当前内存空间是否足够,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。弱引用可以和一个引用队列(ReferenmceQueue)联合使用,如果弱引用所引起的对象被垃圾回收器回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。使用示例如下

public class TestWeak {
    public static void main(String[] args) {     
        WeakReference<String> wr = new WeakReference<String>(new String("hello"));        
        System.out.println(wr.get());
        System.gc();                //通知JVM的gc进行垃圾回收
        System.out.println(wr.get());
    }
}

2.4虚引用(PhantomReference)

虚引用,顾名思义,就是形同虚设,与其他几种引用不同,虚引用并不会决定对象的声明周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别就在于:虚引用必须和引用队列(ReferenceQueue)联合使用,当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以再所引用的对象的内存被回收之前采取必要的。使用示例如下

public class TestPhantom {
    public static void main(String[] args) {
        ReferenceQueue<String> queue = new ReferenceQueue<String>();
        PhantomReference<String> pr = new PhantomReference<String>(new String("hello"), queue);
        System.out.println(pr.get());
    }
}

以上内容需要注意这个概念,一个对象并不是只能持有一种类型的引用,可能强软弱虚引用都有。


三.几种引用的使用场景和注意事项

3.1强引用

关于强引用的使用场景就不说了,基本日常工作中都是用到的这个。


3.2软引用

软引用可以用来实现内存敏感的高速缓存,例如浏览器的后退按钮,按后退时,这个后退需要显示的网页内容是重新进行请求还是从缓存中取出呢,这就要看具体的实现策略了。

(1) 如果一个网页在浏览结束时就进行内容的回收,那么按后退查看前面浏览页面的时候又需要全部重新请求

(2)如果将浏览过的网页存储到内存中就会造成内存的大量浪费,甚至造成内存溢出

这个时候就可以使用软引用

Browser prev = new Browser();               // 获取页面进行浏览
SoftReference sr = new SoftReference(prev); // 浏览完毕后置为软引用    
if(sr.get()!=null){ 
    rev = (Browser) sr.get();           // 还没有被回收器回收,直接获取
}else{
    prev = new Browser();               // 由于内存吃紧,所以对软引用的对象回收了
    sr = new SoftReference(prev);       // 重新构建
}
这样就能很好的解决实际问题。

3.3弱引用

当你想引用一个对象,但是这个对象有自己的生命周期,你不想介入这个对象的生命周期,这个时候就可以使用弱引用,这个引用不会在对象的垃圾回收判断中产生任何附加的影响

如下

public class ReferenceTest {

    private static ReferenceQueue<VeryBig> rq = new ReferenceQueue<VeryBig>();

    public static void checkQueue() {
        Reference<? extends VeryBig> ref = null;
        while ((ref = rq.poll()) != null) {
            if (ref != null) {
                System.out.println("In queue: "    + ((VeryBigWeakReference) (ref)).id);
            }
        }
    }

    public static void main(String args[]) {
        int size = 3;
        LinkedList<WeakReference<VeryBig>> weakList = new LinkedList<WeakReference<VeryBig>>();
        for (int i = 0; i < size; i++) {
            weakList.add(new VeryBigWeakReference(new VeryBig("Weak " + i), rq));
            System.out.println("Just created weak: " + weakList.getLast());

        }

        System.gc(); 
        try { // 下面休息几分钟,让上面的垃圾回收线程运行完成
            Thread.currentThread().sleep(6000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        checkQueue();
    }
}

class VeryBig {
    public String id;
    // 占用空间,让线程进行回收
    byte[] b = new byte[2 * 1024];

    public VeryBig(String id) {
        this.id = id;
    }

    protected void finalize() {
        System.out.println("Finalizing VeryBig " + id);
    }
}

class VeryBigWeakReference extends WeakReference<VeryBig> {
    public String id;

    public VeryBigWeakReference(VeryBig big, ReferenceQueue<VeryBig> rq) {
        super(big, rq);
        this.id = big.id;
    }

    protected void finalize() {
        System.out.println("Finalizing VeryBigWeakReference " + id);
    }
}
输出结果为:
Just created weak: com.javabase.reference.VeryBigWeakReference@1641c0
Just created weak: com.javabase.reference.VeryBigWeakReference@136ab79
Just created weak: com.javabase.reference.VeryBigWeakReference@33c1aa
Finalizing VeryBig Weak 2
Finalizing VeryBig Weak 1
Finalizing VeryBig Weak 0
In queue: Weak 1
In queue: Weak 2
In queue: Weak 0

3.4虚引用

这个在实际中基本很少使用,清楚2.4相关的概念即可。


四.JDK中的ReferenceQueue

在上面中多次提到了ReferenceQueue,可能对这个类的概念还不太清晰,那么接下来对ReferenceQueue做一个简单的讲解。

举个栗子:你从超市买了各种小工具放在在房间不同的柜子里,每个柜子有一把钥匙。如果发现有的小工具很有用,你就一直留着,如果发现有的没用,就把这个丢到垃圾桶,由清洁工人收走,如果这个工具被扔到垃圾桶,想要再把它捡回来使用就不可能了。但是有时候,可能碰到这个工具虽然占着你柜子的空间,而且暂时没用,但是立刻扔掉也不划算,因为将来可能还会派上用场,对于这种可有可无的物品,一种方法就是,如果柜子空间足够,就先把它保留在柜子里,如果柜子里空间不够,再扔掉这些可有可无的物品。这个时候我们把这种可有可无需要暂时保留的物品(对象)对应柜子的钥匙(引用)放在一个包(引用队列)里。如果需要用到就去这个包里取,再打开对应的柜子看是否这个物品还在,如果不在就说明可能之前空间不足或者其他原因被人丢掉了。


五.总结


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值