引用类型
◆ ◆ ◆ ◆
Java提供4个级别的引用:强引用、软引用、弱引用、虚引用。只有强引用FinalReference类是包内可见,其他三种引用类型都是public,可以在程序中直接使用。
强引用
◆ ◆ ◆ ◆
通过引用,可以对堆中的对象操作。在某函数中,当创建了一个对象,该对象被分配堆中,通过这个对象的引用才能对这个对象进行操作。比如:
String str = new String("word");
假设以上代码是在函数体中运行,那么局部变量str将被分配到栈上,而对象String的实例被分配到堆上。局部变量str指向实例所在堆空间,通过str可以操纵实例,那么str就是String的引用,如图所示:
如果此时运行一个赋值语句:
String str1 = str;
那么,str所指向的对象也将被str1所指向,同时在局部栈空间上会分配空间存放str1变量,如下图所示,该String的实例就是两个引用。对引用的“==”操作用于表示两个操作数所指向的堆空间地址是否相同,不表示两个操作数所指向的对象是否相等。
以上两个引用都是强引用。
强引用有如下特点:
强引用可以直接访问目标对象
强引用所指向的对象在任何时候都不会被系统所回收。JVM宁愿抛出OOM异常,也不强行回收强引用对象。
强引用可能导致内存泄漏。
软引用
◆ ◆ ◆ ◆
软引用是除了强引用外,最强的引用类型。可以通过java.lang.ref.SoftReference使用软引用。一个持有软引用的对象, 不会被JVM很快回收,JVM会根据当前堆的使用情况判断何时回收。当堆的使用接近阈值时,才会去回收软引用对象。只要有足够的内存空间,软引用可能会在内存中存在很长一段时间。所以软引用可以用于实现对内存敏感的Cache。
如下面实例:
构造这个对象的软引用:
上例中的检测代码如下:
这个实例中,首先构造MyObject对象,并将其赋值给obj变量,构成强引用。使用SoftReference构造这个MyObject对象的软引用softRef,并注册到softQueue引用队列。当softRef被回收时,会被加入softQueue队列。设置obj==null,删除这个强引用,因此系统中就只剩下软引用。此时,显示调用GC,通过软引用的get(),取得MyObject对象实例的强引用,发现对象并没有被回收。说明GC在内存充足的时候不会回收软引用对象。
接着请求一个大块内存,从而产生新一轮GC。此时softRef.get()不再返回MyObject对象,而是null。说明软引用被回收。
弱引用
◆ ◆ ◆ ◆
弱引用是一种被软引用还弱的引用类型。在GC中,发现弱引用,不管堆空间是否充足,都会被回收。但是,由于垃圾回收器的线程通常优先级很低,因此并不一定很快地发现持有弱引用的对象。在这种情况下,弱引用对象可以存在较长时间。一旦一个弱引用对象被垃圾回收器回收,便会加入到一个注册引用队列中。
软引用和弱引用都非常适合来保存那些可有可无的缓存数据。当内存不足时,这些缓存数据将会被回收,不会导致内存溢出。而当内存资源充足时,这些缓存数据又可以长时间存在,加速效率。
虚引用
◆ ◆ ◆ ◆
虚引用是所有引用中最弱的一个。当一个持有虚引用的对象,和没有引用几乎是一样的,随时都可能会垃圾回收器回收。当试图通过虚引用的get()方法取得强引用,始终是失败的。并且虚引用必须和引用队列一起使用,它的作用在于跟踪垃圾回过程。
我们可以看到虚引用的get()源码,返回的永远是null。
当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在垃圾回收后,将这个虚引用加入引用队列,在其关联的虚引用出队前,不会彻底销毁该对象。所以可以通过检查引用队列中是否有相应的虚引用来判断对象是否已经被回收了。
如果一个对象没有强引用和软引用,对于垃圾回收器而言便是可以被清除的,在清除之前,会调用其finalize方法,如果一个对象已经被调用过finalize方法但是还没有被释放,它就变成了一个虚可达对象。
查看往期精选:
本文参考《Java程序性能优化》
升职加薪,点击关注,不要走丢
无