【JVM】Java虚拟机 四种引用

本文详细介绍了Java中的四种引用类型:强引用、软引用、弱引用和虚引用的特点及应用场景,并探讨了它们在垃圾回收机制中的作用。

什么是引用?

引用就是对象的别名,通过引用来操作对象。

String str = new String("abc");

上面的语句不仅创建了新的对象new String("abc");,同时还声明了它的引用是str



引言

Java 中的垃圾回收机制在判断是否回收某个对象的时候,都需要依据“引用”这个概念。
在不同垃圾回收算法中,对引用的判断方式有所不同:

  • 引用计数法: 为每个对象添加一个引用计数器,每当有一个引用指向它时,计数器就加1,当引用失效时,计数器就减1,当计数器为0时,则认为该对象可以被回收(目前在Java中已经弃用这种方式了)。
  • 可达性分析算法: 从一个被称为 GC Roots 的对象开始向下搜索,如果一个对象到GC Roots没有任何引用链相连时,则说明此对象不可用。

JDK1.2 之前,一个对象只有“已被引用”和"未被引用"两种状态,这将无法描述某些特殊情况下的对象,比如,当内存充足时需要保留,而内存紧张时才需要被抛弃的一类对象。
所以在 JDK.1.2 之后,Java 对引用的概念进行了扩充,将引用分为了:强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference)4 种,这 4 种引用的强度依次减弱。



一、强引用(Strong Reference)

1. 定义

Java中默认声明的就是强引用。强引用是用来描述那些必须的对象。

    private static void testStrongReference() {
        Object object = new Object();
        Object[] objects = new Object[1000];
        String str = new String("abc");
    }

当执行到 testStrongReference() 方法的时候,以上这些都是强引用。

2. 垃圾回收

只要强引用存在,垃圾回收器将永远不会回收被引用的对象,哪怕内存不足时,JVM也会直接抛出OutOfMemoryError,不会去回收。如果想中断强引用与对象之间的联系,可以显示的将强引用赋值为null,这样一来,JVM就可以适时的回收对象了。



二、软引用(Soft Reference)

1. 定义

使用 SoftReference 包装过了的对象。软引用是用来描述一些还有用但并非必须的对象。

public class Demo1 {
	public static void main(String[] args) {
		final int _4M = 4*1024*1024;
		byte[] cacheData = new byte[100 * 1024 * 1024];
        SoftReference<byte[]> cacheRef = new SoftReference<>(cacheData);
	}
}

如果在垃圾回收时发现内存不足,在回收软引用所指向的对象时,软引用本身不会被清理 如果想要清理软引用,需要使用引用队列

public class Demo1 {
	public static void main(String[] args) {
		final int _4M = 4*1024*1024;
		//使用引用队列,用于移除引用为空的软引用对象
		ReferenceQueue<byte[]> queue = new ReferenceQueue<>();
		//使用软引用对象 list和SoftReference是强引用,而SoftReference和byte数组则是软引用
		List<SoftReference<byte[]>> list = new ArrayList<>();
		SoftReference<byte[]> ref= new SoftReference<>(new byte[_4M]);

		//遍历引用队列,如果有元素,则移除
		Reference<? extends byte[]> poll = queue.poll();
		while(poll != null) {
			//引用队列不为空,则从集合中移除该元素
			list.remove(poll);
			//移动到引用队列中的下一个元素
			poll = queue.poll();
		}
	}
}

2. 垃圾回收

如果一个对象具有软引用,只要内存空间足够,垃圾回收器就不会回收它。如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。

/**
 * 软引用何时被收集
 * 运行参数 -Xmx200m -XX:+PrintGC
 * Created by ccr at 2018/7/14.
 */
public class SoftReferenceDemo {

    public static void main(String[] args) throws InterruptedException {
        //100M的缓存数据
        byte[] cacheData = new byte[100 * 1024 * 1024];
        //将缓存数据用软引用持有
        SoftReference<byte[]> cacheRef = new SoftReference<>(cacheData);
        //将缓存数据的强引用去除
        cacheData = null;
        System.out.println("第一次GC前" + cacheData);
        System.out.println("第一次GC前" + cacheRef.get());
        //进行一次GC后查看对象的回收情况
        System.gc();
        //等待GC
        Thread.sleep(500);
        System.out.println("第一次GC后" + cacheData);
        System.out.println("第一次GC后" + cacheRef.get());

        //在分配一个120M的对象,看看缓存对象的回收情况
        byte[] newData = new byte[120 * 1024 * 1024];
        System.out.println("分配后" + cacheData);
        System.out.println("分配后" + cacheRef.get());
    }

}

第一次GC前null
第一次GC前[B@7d4991ad
[GC (System.gc())  105728K->103248K(175104K), 0.0009623 secs]
[Full GC (System.gc())  103248K->103139K(175104K), 0.0049909 secs]
第一次GC后null
第一次GC后[B@7d4991ad
[GC (Allocation Failure)  103805K->103171K(175104K), 0.0027889 secs]
[GC (Allocation Failure)  103171K->103171K(175104K), 0.0016018 secs]
[Full GC (Allocation Failure)  103171K->103136K(175104K), 0.0089988 secs]
[GC (Allocation Failure)  103136K->103136K(199680K), 0.0009408 secs]
[Full GC (Allocation Failure)  103136K->719K(128512K), 0.0082685 secs]
分配后null
分配后null

从上面的示例中就能看出,软引用关联的对象不会被GC回收。JVM在分配空间时,若果Heap空间不足,就会进行相应的GC,但是这次GC并不会收集软引用关联的对象,但是在JVM发现就算进行了一次回收后还是不足(Allocation Failure),JVM会尝试第二次GC,回收软引用关联的对象。

3. 使用场景

软引用可用来实现内存敏感的高速缓存,比如网页缓存、图片缓存等。使用软引用能防止内存泄露,增强程序的健壮性。



三、弱引用(Weak Reference)

1. 定义

使用 WeakReference 包装过了的对象。

public class Demo1 {
	public static void main(String[] args) {
		final int _4M = 4*1024*1024;
		byte[] cacheData = new byte[100 * 1024 * 1024];
        WeakReference<byte[]> cacheRef = new WeakReference<>(cacheData);
	}
}

2. 垃圾回收

如果一个对象具有弱引用,无论内存是否充足,只要JVM垃圾回收开始,都会回收它。

/**
 * 弱引用关联对象何时被回收
 * Created by ccr at 2018/7/14.
 */
/**
 * 弱引用关联对象何时被回收
 * Created by ccr at 2018/7/14.
 */
public class WeakReferenceDemo {
    public static void main(String[] args) throws InterruptedException {
        //100M的缓存数据
        byte[] cacheData = new byte[100 * 1024 * 1024];
        //将缓存数据用软引用持有
        WeakReference<byte[]> cacheRef = new WeakReference<>(cacheData);
        System.out.println("第一次GC前" + cacheData);
        System.out.println("第一次GC前" + cacheRef.get());
        //进行一次GC后查看对象的回收情况
        System.gc();
        //等待GC
        Thread.sleep(500);
        System.out.println("第一次GC后" + cacheData);
        System.out.println("第一次GC后" + cacheRef.get());

        //将缓存数据的强引用去除
        cacheData = null;
        System.gc();
        //等待GC
        Thread.sleep(500);
        System.out.println("第二次GC后" + cacheData);
        System.out.println("第二次GC后" + cacheRef.get());
    }
}
第一次GC前[B@7d4991ad
第一次GC前[B@7d4991ad
第一次GC后[B@7d4991ad
第一次GC后[B@7d4991ad
第二次GC后null
第二次GC后null


四、虚引用

1. 定义

虚引用是最弱的一种引用关系,如果一个对象仅持有虚引用,那么它就和没有任何引用一样,它随时可能会被回收,在 JDK1.2 之后,用 PhantomReference 类来表示,通过查看这个类的源码,发现它只有一个构造函数和一个 get() 方法,而且它的 get() 方法仅仅是返回一个null,也就是说将永远无法通过虚引用来获取对象,虚引用必须要和 ReferenceQueue 引用队列一起使用。
虚引用的一个体现是释放直接内存所分配的内存,当引用的对象ByteBuffer被垃圾回收以后,虚引用对象Cleaner就会被放入引用队列中,然后调用Cleaner的clean方法来释放直接内存

参考:
黑马程序员JVM完整教程,全网超高评价,全程干货不拖沓
Java篇 - 四种引用(Reference)实战
软引用、弱引用、虚引用-他们的特点及应用场景
Java四种引用类型
java官方api

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值