四种引用类型:强引用、软引用、弱引用、虚引用

java中除了基本数据类型的变量(int、long等),剩下的都是引用类型的变量,一共有四种不同的引用类型。

一、强引用(Strong Reference)

强引用就是最常见的对某个对象的引用,如下代码变量o就是对所创建的Object对象的一个强引用。

Object o = new Object();

存在强引用的对象,不会被垃圾回收,即便发生了OutOfMemoryError,我们来看如下的测试代码:

/**
 * 这是一个大对象
 * @author 白吃的午餐
 *
 */
public class BigObject {
	private String name;
	private int[] a = new int[1024];

	public BigObject(String name) {
		this.name = name;
	}
	
	@Override
	public String toString() {
		return "BigObject [name=" + name + "]";
	}
}

public class StrongReferenceTest {

	public static void main(String[] args) {
		List bigs = new LinkedList();
		
		for(int i=0; i<1000; i++) {
			BigObject bo = new BigObject("BigObject_" + i);
			bigs.add(bo);
		}
	}

}

输出:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at com.lizhihong.reference.BigObject.(BigObject.java:5)
    at com.lizhihong.reference.StrongReferenceTest.main(StrongReferenceTest.java:12)

二、软引用(Soft Reference)

软引用是强度仅次于强引用的一种引用类型,当JVM认为内存不足时,会回收软引用所指向的对象

public class SoftReferenceTest {

	public static void main(String[] args) throws InterruptedException {
		List> bigs = new LinkedList>();
		
		ReferenceQueue rq = new ReferenceQueue();
		BigObject bo = new BigObject("BigObject");
		SoftReference sr = new SoftReference(bo, rq);
		bo = null;
		
		System.gc();
		//内存充足,对象不会被回收,仍然可以被获取到
		System.out.println(sr.get());
		
		//内存不足时,会回收软引用指向的对象,不会抛出OutOfMemoryError
		for(int i=0; i<1000; i++) {
			BigObject bo2 = new BigObject("BigObject_" + i);
			SoftReference sr2 = new SoftReference(bo2);
			bigs.add(sr2);
			bo = null;
		}
		
		//对象已经被回收,返回null
		System.out.println(sr.get());
	}
}

输出:

BigObject [name=BigObject]
null

三、弱引用(Weak Reference)

弱引用是强度次于强引用和软引用的一种引用类型,JVM每次GC时,都有可能回收弱引用指向的对象

public class WeakReferenceTest {

	public static void main(String[] args) {
		BigObject bo = new BigObject("BigObject_0");
		WeakReference wr = new WeakReference(bo);
		bo = null;
		System.gc();
		//与软引用不同,jvm每次gc时都有可能回收弱引用指向的对象,此处输出为null
		System.out.println(wr.get());
	}

}

输出

null

四、虚引用(Phantom Reference)

虚引用的概念比较难理解,你不能通过一个虚引用访问它指向的对象,PhantomReference的get方法永远返回null,仅仅是提供了对象被实际回收前做某些事情的机制,有点类似于finalize方法,但这个机制其实发生在finalize之后。

首先我们来澄清一个说法:虚引用是强度小于弱引用的一种引用类型

这个说法我个人觉得不完全正确,我们来看下面的这段代码会输出什么。如果虚引用是强度更弱的一种引用类型,下面这段代码应该不会报错,因为GC会正常回收虚引用指向的对象,就像软引用和弱引用一样,但实际执行的情况呢

public class PhantomReferenceTest1 {

	public static void main(String[] args) {
		List> bigs = new LinkedList>();
		ReferenceQueue rq = new ReferenceQueue();
		
		for(int i=0; i<1000; i++) {
			BigObject bo = new BigObject("BigObject_" + i);
			PhantomReference pr = new PhantomReference(bo, rq);
			bigs.add(pr);
			bo=null;
		}
	}
	
}

输出:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at com.lizhihong.reference.BigObject.(BigObject.java:10)
    at com.lizhihong.reference.PhantomReferenceTest1.main(PhantomReferenceTest1.java:16)

OutOfMemoryError,说明虚引用指向的对象并没有被回收,这是为什么?

下面来说说我的理解,如有错误的地方,还请大家指教

1、以弱引用类型举例(软引用类似),当我们创建一个弱引用的时候,最终会调用Reference如下的构造函数

private T referent;         /* Treated specially by GC */
    
Reference(T referent, ReferenceQueue queue) {
        this.referent = referent;
        this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
    }

referent,分明就是一个强引用,它所指向的对象为什么会被回收呢

2、当JVM执行GC,回收弱引用所指向的对象时,实际上JVM会先把referent置为null,这样此前referent指向的对象就没有任何引用了,可以被JVM回收,实际情况正式这样的,我们来看下WeakReference的java doc说明

/**
 * Weak reference objects, which do not prevent their referents from being
 * made finalizable, finalized, and then reclaimed.  Weak references are most
 * often used to implement canonicalizing mappings.
 *
 *

Suppose that the garbage collector determines at a certain point in time
 * that an object is weakly
 * reachable
.  At that time it will atomically clear all weak references to
 * that object
and all weak references to any other weakly-reachable objects
 * from which that object is reachable through a chain of strong and soft
 * references.  At the same time it will declare all of the formerly
 * weakly-reachable objects to be finalizable.  At the same time or at some
 * later time it will enqueue those newly-cleared weak references that are
 * registered with reference queues.
 *
 * @author   Mark Reinhold
 * @since    1.2
 */

3、为什么虚引用指向的对象没有被回收,同样我们查看PhantomReference的java doc说明

/**
 * Phantom reference objects, which are enqueued after the collector
 * determines that their referents may otherwise be reclaimed.  Phantom
 * references are most often used for scheduling pre-mortem cleanup actions in
 * a more flexible way than is possible with the Java finalization mechanism.
 *
 *

If the garbage collector determines at a certain point in time that the
 * referent of a phantom reference is  * href="package-summary.html#reachability">phantom reachable, then at that
 * time or at some later time it will enqueue the reference.
 *
 *

In order to ensure that a reclaimable object remains so, the referent of
 * a phantom reference may not be retrieved: The get method of a
 * phantom reference always returns null.
 *
 *

Unlike soft and weak references, phantom references are not
 * automatically cleared by the garbage collector as they are enqueued
.  An
 * object that is reachable via phantom references will remain so until all
 * such references are cleared or themselves become unreachable.
 *
 * @author   Mark Reinhold
 * @since    1.2
 */

4、虚引用指向的对象什么时候被回收,这取决于JVM

你可能感兴趣的:(Java基础)