java的强、软、弱、虚引用详解

前言:

        在JDK 1.2以前的版本中,若一个对象不被任何变量引用,那么程序就无法再使用这个对象。也就是说,只有对象处于可触及(reachable)状态,程序才能使用它。从JDK 1.2版本开始,把对象的引用分为4种级别,从而使程序能更加灵活地控制对象的生命周期。这4种级别由高到低依次为:强引用软引用弱引用虚引用

强引用:

        强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。如下:

    Object o=new Object();   //  强引用
        当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。如果不使用时,要通过如下方式来弱化引用,如下:
    o=null;     // 帮助垃圾收集器回收此对象

        强引用在实际中有非常重要的用处,下面是ArrayList的实现源代码:

/**
* clear方法,清空ArrayList实例中的元素
*/
public void clear() {
	modCount++;
	// Let gc do its work
	for (int i = 0; i < size; i++)
		elementData[i] = null;
	size = 0;
}
        在ArrayList类中定义了一个私有的变量elementData数组,在调用方法清空数组时可以看到为每个数组内容赋值为null。不同于elementData=null,强引用仍然存在,避免在后续调用 add()等方法添加元素时进行重新的内存分配。使用如clear()方法中释放内存的方法对数组中存放的引用类型特别适用,这样就可以及时释放内存。 

软引用:

        如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。      
String str=new String("a");                                     // 强引用
SoftReference<String> softRef=new SoftReference<String>(str);     // 软引用

        当内存不足时,等价于:

If(JVM.内存不足()) {
   str = null;  // 转换为软引用
   System.gc(); // 垃圾回收器进行回收
}
        这里需要注意一点,调用System.gc()时,垃圾回收器不一定会立刻执行垃圾回收。

        SoftReference的特点是它的一个实例保存对一个Java对象的软引用,该软引用的存在不妨碍垃圾收集线程对该Java对象的回收。也就是说,一旦SoftReference保存了对一个Java对象的软引用后,在垃圾线程对这个Java对象回收前,SoftReference类所提供的get()方法返回Java对象的强引用。另外,一旦垃圾线程回收该Java对象之后,get()方法将返回null。
        看下面代码:
MyObject aRef = new  MyObject();
SoftReference aSoftRef=new SoftReference(aRef);
        此时,对于这个MyObject对象,有两个引用路径,一个是来自SoftReference对象的软引用,一个来自变量aReference的强引用,所以这个MyObject对象是强可及对象。
        随即,我们可以结束aReference对这个MyObject实例的强引用:
aRef = null;
        此后,这个MyObject对象成为了软可及对象。如果垃圾收集线程进行内存垃圾收集,并不会因为有一个SoftReference对该对象的引用而始终保留该对象。Java虚拟机的垃圾收集线程对软可及对象和其他一般Java对象进行了区别对待:软可及对象的清理是由垃圾收集线程根据其特定算法按照内存需求决定的。也就是说,垃圾收集线程会在虚拟机抛出OutOfMemoryError之前回收软可及对象,而且虚拟机会尽可能优先回收长时间闲置不用的软可及对象,对那些刚刚构建的或刚刚使用过的“新”软可反对象会被虚拟机尽可能保留。在回收这些对象之前,我们可以通过:
MyObject anotherRef=(MyObject)aSoftRef.get();
        重新获得对该实例的强引用。而回收之后,调用get()方法就只能得到null了。

弱引用:

        弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。 


虚引用:

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


代码实验:

创建了两个Object类型数组,长度总共400000000,堆内存溢出
public void test1(int capacity1,int capacity2){
	Object[] objects1 = new Object[capacity1];
	Object[] objects2 = new Object[capacity2];
	System.out.println(objects1);
}
public static void main(String[] args) {
	int a = 300000000,b = 100000000;
	new Main().test1(a,b);
}


同样创建了两个Object类型数组,第一个是软引用数组,在执行到创建第二个数组的时候,堆内存不够用了,所以将第一个数组回收了

public void test2(int capacity1,int capacity2){
	SoftReference sr = new SoftReference(new Object[capacity1]);
	System.out.println("sr:" + sr.get());
	Object[] objects = new Object[capacity2];
	System.out.println("回收了" + sr.get());
}
public static void main(String[] args) {
	int a = 300000000,b = 100000000;
	new Main().test2(a,b);
}

与上一个方法不同的是:在第二个数组创建之前,取出了sr中的元素,obj为强引用,所以堆内存溢出

public void test2_1(int capacity1,int capacity2){
	SoftReference sr = new SoftReference(new Object[capacity1]);
	Object[] obj = (Object[]) sr.get();
	System.out.println("sr:" + sr.get());
	Object[] objects = new Object[capacity2];
	System.out.println("回收了" + sr.get());
}
public static void main(String[] args) {
	int a = 300000000,b = 100000000;
	new Main().test2_1(a,b);
}

软引用和弱引用的区别

public void test4() {
	try {
		//软引用
		SoftReference sr = new SoftReference(new Object[1]);
		//弱引用
		WeakReference wr = new WeakReference(new Object[1]);
		//System.gc()不会立即执行垃圾回收  线程等5s
		System.gc();
		Thread.sleep(5000);
		System.out.println(sr.get());
		System.out.println(wr.get());
	} catch (InterruptedException e) {
		e.printStackTrace();
	}
}


使用ReferenceQueue清除失去了软引用对象的SoftReference

        作为一个Java对象,SoftReference对象除了具有保存软引用的特殊性之外,也具有Java对象的一般性。所以,当软可及对象被回收之后,虽然这个SoftReference对象的get()方法返回null,但这个SoftReference对象已经不再具有存在的价值,需要一个适当的清除机制,避免大量SoftReference对象带来的内存泄漏。在java.lang.ref包里还提供了ReferenceQueue。如果在创建SoftReference对象的时候,使用了一个ReferenceQueue对象作为参数提供给SoftReference的构造方法,如:
ReferenceQueue queue = new  ReferenceQueue();
SoftReference  ref=new  SoftReference(aMyObject, queue);
        那么当这个SoftReference所软引用的aMyOhject被垃圾收集器回收的同时,ref所强引用的SoftReference对象被列入ReferenceQueue。也就是说,ReferenceQueue中保存的对象是Reference对象,而且是已经失去了它所软引用的对象的Reference对象。另外从ReferenceQueue这个名字也可以看出,它是一个队列,当我们调用它的poll()方法的时候,如果这个队列中不是空队列,那么将返回队列前面的那个Reference对象。
        在任何时候,我们都可以调用ReferenceQueue的poll()方法来检查是否有它所关心的非强可及对象被回收。如果队列为空,将返回一个null,否则该方法返回队列中前面的一个Reference对象。利用这个方法,我们可以检查哪个SoftReference所软引用的对象已经被回收。于是我们可以把这些失去所软引用的对象的SoftReference对象清除掉。常用的方式为:
SoftReference ref = null;
while ((ref = (EmployeeRef) q.poll()) != null) {
    // 清除ref
}

级别
什么时候被垃圾回收
用途
生存时间
强引用
从来不会
对象的一般状态
JVM停止运行时终止
软引用
在内存不足时
对象简单?缓存
内存不足时终止
弱引用
在垃圾回收时
对象缓存
gc运行后终止
虚引用
Unknown
Unknown
Unknown

参考链接:https://www.cnblogs.com/gudi/p/6403953.html

                https://blog.csdn.net/coding_or_coded/article/details/6603549

                https://blog.csdn.net/haixia_12/article/details/51888707

                https://blog.csdn.net/heminghung/article/details/51379143

                https://www.cnblogs.com/yw-ah/p/5830458.html

猜你喜欢

转载自blog.csdn.net/xcy1193068639/article/details/80943692