强引用、软引用、弱引用和虚引用

1 篇文章 0 订阅

总结

强引用:对象不可达时才回收的对象。例如我们平时创建的对象Object obj=new Object()
软引用:当虚拟机判断快要内存溢出时就进行回收的对象。一般用户那些重要性不高的缓存。
弱引用:发生gc时就回收的对象。
虚引用:为一个对象设置虚引用的唯一目的只是为了能在这个对象被回收时收到一个系统的通知。虚引用不影响对象生命周期,跟没有引用一样。

具体分析

强引用

强引用其实不必多说,我们平时创建的那些对象就是强引用对象。

软引用

软引用的代码示例

		SoftReference<Object> wo = new SoftReference<Object>(new Object()); 
        /**
            如果这样写无效,这是由于还有一个o对象的强引用指向它
            Object o=new Object();
            SoftReference<Object> wo = new SoftReference<Object>(o); 
        **/

注意的点:像注释里写的那样,如果一个软引用的对象被其它的对象强引用着,则无法被回收。
通过get()方法可以获取到软引用的对象。

应用场景

全局的catch
一般我们使用全局缓存catch都会这样,
Object catch=new Object()或HashMap<String,String> map=new HashMap<String,String>()
这种全局变量如果我们不手动在代码中使 Object catch=null手动取消引用就一直不会被gc回收,如果数量太多容易导致内存溢出。所以对于全局的变量如果不是那种必须一直保持的,可以定义为弱引用(每次gc都会回收)或软引用(快内存溢出时才回收)

弱引用

弱引用只要发生gc垃圾回收就会被回收。
弱引用的写法也类似软引用

		WeakReference<Object> wo = new WeakReference<Object>(new Object()); 
        /**
            如果这样写无效,这是由于还有一个o对象的强引用指向它
            Object o=new Object();
            WeakReference<Object> wo = new WeakReference<Object>(o); 
        **/
        if (wo.get() != null) {
            System.out.println("not null before gc");
        }
        System.gc();
        if (wo.get() != null) {
            System.out.println("not null after gc");
        }
        else {
            System.out.println("null after gc");
        }		

通过get()方法可以获取到弱引用的对象。WeakHashMap 是HashMap的弱引用版

应用场景

同软引用一样也可作为缓存,但弱引用生命周期比软引用短。

虚引用

虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。如果发现该队列中有该虚引用对象就说明快要被垃圾回收了,那么就可以在所引用的对象的内存被回收之前采取必要的行动。

Object obj = new Object();
ReferenceQueue refQueue = new ReferenceQueue();//引用队列
// 关联引用队列和虚引用对象,在垃圾回收后会被加入引用队列,可以使用循环线程监听,refQueue.poll()
//返回是否为空来判断有没有对象被回收了
PhantomReference<Object> phantomReference = new PhantomReference<Object>(obj,refQueue);

应用场景

可以用来在垃圾回收后做一些操作。

finalize和虚引用

finalize是Object方法中的,可通过重写该方法来在垃圾回收前做些处理。(不建议使用)
finalize流程如下
1、当一个不可达对象重写了finalize,gc回收器发现其重写了finalize,需要执行其方法,会将其放入finalization queue 队列中,由Finalizer的finalizer daemon thread线程负责执行。
2、放入队列中的不可达对象会被finalizer daemon thread后台线程的Finalizer class引用,这时就变成强引用,垃圾回收线程就不能对其进行回收,所以其实第一轮的垃圾回收线程是无法回收重写了finalize的对象的,因为finalizer daemon thread的优先级较低,所以垃圾回收线程优先完成,finalize还没执行,一直是强引用
3、只有当finalize执行完后,强引用断掉,变成不可达对象,在下一次gc时该对象才会被回收。
总结:finalize的对象需要经历至少两次gc才能被回收(一般来说是两次,除非像下面代码一样手动调用使两次gc间隔很短,导致第二次gc无法回收)。如果大量对象都是这样容易造成内存溢出,所以避免使用。

代码示例

public class Test extends Thread {
    ReferenceQueue refQueue ;//引用队列
    @Override
    public void run()
    {
        // 这里循环从虚引用队列中取值判断对象是否已回收
        for(;;){
            Reference reference= refQueue.poll();
            if(reference!=null){
                System.out.println("已回收");
            }
        }
    }

    public void setRefQueue(ReferenceQueue refQueue){
        this.refQueue=refQueue;
    }
    public static void main(String[] args) {
        Student stu = new Student();
        ReferenceQueue refQueue = new ReferenceQueue();//引用队列
        PhantomReference<Object> phantomReference = new PhantomReference<Object>(stu, refQueue);
        Test test = new Test();
        test.setRefQueue(refQueue);
        test.start();
        stu = null;
        System.gc();
        /**
        **这里为什么要休眠100毫秒,为了防止finalizer daemon thread线程
        **还未从队列中获取并执行finalize方法第二次gc就先执行了,这样对象错过第二次gc回收
        **/
        try{Thread.sleep(100);}  
        catch (Exception e){}
        System.gc();
    }
}
public class Student {
    @Override
    protected void finalize() throws Throwable {
        System.out.println("执行finalize");
    }
}

结果
执行finalize
已回收(如果不休眠100毫秒,错过第二次gc就不会回收)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值