欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

java引用类型分析

程序员文章站 2022-06-10 13:43:39
...

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:aaa@qq.com

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());
    }
}

输出结果:

java引用类型分析

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());
    }
}

输出结果:

java引用类型分析

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
			}
		}
	}
}