java引用类型分析

java的引用类型

1、Reference

引用对象的抽象基类,定义了所有引用对象常见的操作方法。

// 构造函数
Reference(T referent) {
	this(referent, null);
}

// 构造函数,定义引用队列
Reference(T referent, ReferenceQueue<? super T> queue) {
	this.referent = referent;
	this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
}

// 返回这个引用对象的referent,垃圾收集器回收后返回null
public T get() {
	return this.referent;
}

public void clear() {
	this.referent = null;
}

// 是否入队
public boolean isEnqueued() {
	return (this.queue == ReferenceQueue.ENQUEUED);
}

// 入队方法
public boolean enqueue() {
	return this.queue.enqueue(this);
}

2、ReferenceQueue

当垃圾回收器判断引用对象的可达性改变时(变成不可达),将其注册(入队)到应用队列中。

对其引用感兴趣的程序可以通过引用队列的poll、remove方法获取该引用。

// 入队
boolean enqueue(Reference<? extends T> r)

// 出队(非阻塞)
public Reference<? extends T> poll() 

// 出队(阻塞)
public Reference<? extends T> remove()

3、WeakReference

垃圾回收器GC时,会回收弱引用绑定的对象,在同一时间或晚些时候将弱引用注册到引用队列。先清除对象,在入队列。

下面的测试代码说明弱引用的生命周期

public class Test<T extends Reference> {
    static ReferenceQueue queue = new ReferenceQueue();

    public T creatRefObject() {
        Reference ref = new WeakReference(new Object(), queue);
        return (T) ref;
    }

    public static void main(String[] args) throws Exception {
        Test test = new Test();
        Reference ref = test.creatRefObject();
        System.gc();
        // 引用中绑定的对象为null
        System.out.println("reference get:" + ref.get());
        // 获取队列中引用对象
        System.out.println("queue poll:" + queue.remove());

    }

}

输出结果:

reference get:null

queue poll:java.lang.ref.WeakReference@123772c4

4、SoftReference

当内存不足时(即将OOM时),会回收软引用绑定的对象,在同一时间或晚些时候将软引用注册到引用队列。先清除对象,在入队列。

下面的测试代码说明软引用的生命周期,需要设置运行时vm变量:-Xms20m -Xmx20m,模拟内存溢出。

public class Test<T extends Reference> {
    static ReferenceQueue queue = new ReferenceQueue();

    public T creatRefObject() {
        Reference ref = new SoftReference(new Object(), queue);
        return (T) ref;
    }
    
    private void oom() {
        try {
            List list = new ArrayList();
            while (true) {
                list.add(new Object());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) throws Exception {
        Test test = new Test();
        Reference ref = test.creatRefObject();
        System.gc();
        // 引用中绑定的对象不为空
        System.out.println("reference get:" + ref.get());
        // 制造内存溢出
        new Thread(()->{test.oom();}).start();
        Thread.sleep(10000);
        // 此时引用绑定对象为null
        System.out.println("reference get:" + ref.get());
        // 获取队列中引用对象
        System.out.println("queue poll:" + queue.remove());
    }
}

输出结果:


5、PhantomReference

与弱引用、软引用不同,虚引用对象不会自动回收(入队时并没有被垃圾回收器自动清除)。虚引用绑定的对象仍然保持原有状态,直到所有的引用都被清除,或者不可到达。

为了保持引用对象的原有状态,虚引用的get方法永远返回null

    /**
     * Returns this reference object's referent.  Because the referent of a
     * phantom reference is always inaccessible, this method always returns
     * <code>null</code>.
     *
     * @return  <code>null</code>
     */
    public T get() {
        return null;
    }

下面的测试代码说明虚引用的生命周期

public class Test<T extends Reference> {
    static ReferenceQueue queue = new ReferenceQueue();

    public static class A {
        public void print() {
            System.out.println("I'am live!");
        }

//        @Override
//        protected void finalize() throws Throwable {
//            super.finalize();
//            System.out.println("I'am finalize()!");
//        }
    }

    public T creatRefObject() {
        A a = new A();
        Reference ref = new PhantomReference(a, queue);
        a.print();
        return (T) ref;
    }

    public static void main(String[] args) throws Exception {
        Test test = new Test();
        Reference ref = test.creatRefObject();
        // gc先后都为null
        System.out.println("ref get:" + ref.get());
        System.gc();
        System.out.println("ref get:" + ref.get());
        Thread.sleep(500);
        // 如果重写finalize方法需要再次调用gc
//        System.gc();
        // 获取队列中引用对象
        System.out.println("queue poll:" + queue.remove());
    }
}

输出结果:


6、Finalizer

当对象被标记回收时,如果重写了finalize方法,并且finalize方法没有被调用过,那么这个对象将会放置在一个叫做F-Queue的队列之中,低优先级的Finalizer线程去执行。

finalize()方法是对象逃脱死亡命运的最后一次机会,稍后GC将对F-Queue中的对象进行第二次小规模的标记,如果对象要在finalize()中成功拯救自己——只要重新与引用链上的任何一个对象建立关联即可,譬如把自己(this关键字)赋值给某个类变量或者对象的成员变量,那在第二次标记时它将被移除出“即将回收”的集合;如果对象这时候还没有逃脱,那基本上它就真的被回收了。

低优先级的Finalizer线程

private static class FinalizerThread extends Thread {
	private volatile boolean running;
	FinalizerThread(ThreadGroup g) {
		super(g, "Finalizer");
	}
	public void run() {
		if (running)
			return;

		// Finalizer thread starts before System.initializeSystemClass
		// is called.  Wait until JavaLangAccess is available
		while (!VM.isBooted()) {
			// delay until VM completes initialization
			try {
				VM.awaitBooted();
			} catch (InterruptedException x) {
				// ignore and continue
			}
		}
		final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
		running = true;
		for (;;) {
			try {
				// 获取F-Queue对象,执行finalize方法
				Finalizer f = (Finalizer)queue.remove();
				f.runFinalizer(jla);
			} catch (InterruptedException x) {
				// ignore and continue
			}
		}
	}
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值