什么是引用?
引用就是对象的别名,通过引用来操作对象。
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
本文详细介绍了Java中的四种引用类型:强引用、软引用、弱引用和虚引用的特点及应用场景,并探讨了它们在垃圾回收机制中的作用。
223

被折叠的 条评论
为什么被折叠?



