引用类型主要分为强软弱虚四种:(GC是java中的垃圾回收机制)
强引用(StrongReference)
强引用 指的就是代码中普遍存在的赋值方式,比如A a = new A()
这种。强引用关联的对象,永远不会被GC回收。
当内存空间不足的时候,java虚拟机宁愿抛出OutOfMemoryError
错误,是程序异常终止,也不会回收具有强引用的对象来解决内存不足的问题
csharp
obj=null //帮助垃圾回收器回收这个对象
显式地设置obj为null,或者说超出对象的生命周期范围,则GC认为该对象不存在引用,这时候就可以回收这个对象了,具体什么时候回收这要看GC的算法是怎么样的。
当一个方法的内部有一个强引用,这个引用保存在栈中,而这个引用的内容存放在堆中,当这个方法运行完成后就会退出方法栈,那么引用内容也会跟着就会不存在了,这个Object就会被回收。但是如果个这个obj是一个全局变量的时候,就需要再不用的时候将赋值为null,因为强引用是不会被垃圾回收的
在ArrayList的clear方法中就用到了强引用
ini
private transient Object[] elementData;
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++) {
elementData[i] = null;
}
size = 0;
}
在 ArrayList 类中定义了一个私有的变量 elementData 数组,在调用方法清空数组时可以看到为每个数组内容赋值为null。 不同于elementData = null,强引用仍然存在,避免在后续调用 add()等方法添加元素时,进行重新的内存分配。使用如 clear() 方法中释放内存的方法对数组中存放的引用类型特别适用,这样就可以及时释放内存。
软引用(SoftReference)
软引用 可以用SoftReference来描述,指的是那些有用但是不是必须要的对象 。系统在发生内存溢出前会对这类引用的对象进行回收。
软引用可以用来实现内存敏感的高速缓存
软引用自身不会被垃圾回收,因为GC Root还引用着,软引用自身需要配合引用队列来释放。
javascript
String str = new String("abc"); // 强引用
SoftReference<String> softRef = new SoftReference<String>(str); // 软引用
// 当内存不足时,等价于:
if (JVM.内存不足()) {//当内存不够的时候就回收
str = null; // 转换为软引用
System.gc(); // 垃圾回收器进行回收
}
软引用在实际中有重要的应用:
浏览器的后退按钮。按后退时,这个后退时显示的网页内容是重新进行请求还是从缓存中取出呢?这就要看具体的实现策略了。
- 获取页面进行浏览,浏览完成后就可以将页面设置为软应用
- 当点击回退按钮到先前浏览过的页面后,判断是否被垃圾回收机制回收了,没有回收就直接用,如果回收了,就在重新构建页面
软引用可以和一个引用队列(ReferenceQueue)联合使用,当引用的对象被垃圾回收器回收后,JVM会自动把这个软引用加入到和它相关的这个引用队列中
弱引用(WeakReference)
弱引用 可以用WeakReference来描述,他的强度比软引用更低一点 ,弱引用的对象下一次GC的时候一定会被回收 ,而不管内存是否足够。 不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。
弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。
ini
String str = new String("abc"); //强引用
WeakReference<String> weakRef = new WeakReference<String>(str); // 弱引用
str = null;
// 当垃圾回收器进行扫描回收时等价于:
str = null;
System.gc(); // 垃圾回收器进行回收
// 下面的代码会让str再次变为一个强引用:
str = weakRef.get();
使用场景:
- 如果这个对象是偶尔的使用,并且希望在使用时随时就能获取到,但又不想影响此对象的垃圾收集,那么你应该用弱引用来记住此对象。
- 当你想引用一个对象,但是这个对象有自己的生命周期,你不想介入这个对象的生命周期,这时候你就可以使用弱引用。这个引用不会在对象的垃圾回收判断中产生任何附加的影响。
弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。
虚引用(PhantomReference)
虚引用 也被称作幻影引用,是最弱的引用关系,可以用PhantomReference
来描述,他必须和ReferenceQueue
一起使用,同样的当发生GC的时候,虚引用也会被回收。
虚引用与其他几种引用都不同,它并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。
虚引用与软引用和弱引用的一个区别在于: 虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。
应用场景:
- 程序可以通过判断引用队列中是否存在该对象的虚引用,来了解这个对象是否将要被回收。可以用来作为GC回收Object的标志。
- 可以用虚引用来管理堆外内存。
Reference和ReferenceQueue
四大引用的父类Reference和ReferenceQueue
在Reference中有5个非常重要的属性:referent,next,discovered,pending,queue。
java
//5个非常重要的属性
private T referent; /* Treated specially by GC */
volatile ReferenceQueue<? super T> queue;
Reference next;
transient private Reference<T> discovered; /* used by VM */
private static Reference<Object> pending = null;
可以把每个Reference看做一个节点,多个Reference通过next,discovered,pending 进行关联
- referent就是Reference实际引用的对象。
- 通过next属性,可以构建ReferenceQueue。
- 通过discovered属性,可以构建Discovered List。
- 通过pending属性,可以构建Pending List。
四大状态
上图就是Reference 的四个状态
kotlin
//Reference的两个构造方法,一个带队列,一个不带
Reference(T referent) {
this(referent, null);
}
Reference(T referent, ReferenceQueue<? super T> queue) {
this.referent = referent;
this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
}
对于带ReferenceQueue的Reference ,GC会把要回收对象的Reference放到ReferenceQueue中,后续该Reference需要程序员自己处理 (调用poll方法) 。
不带ReferenceQueue的Reference, 由GC自己处理,待回收的对象其Reference状态会变成Inactive。
- 创建好了Reference,就进入active状态。
- active状态下,如果引用对象的可到达状态发送变化就会转变成Inactive或Pending状态。
- Inactive状态很好理解,到达Inactive状态的Reference状态不能被改变,会等待GC回收。
- Pending 状态代表等待入Queue,Reference内部有个ReferenceHandler,会调用enqueue方法,将Pending对象入到Queue中入Queue的对象,其状态就变成了Enqueued。
- Enqueued 状态的对象,如果调用poll方法从ReferenceQueue拿出,则该Reference的状态就变成了Inactive,等待GC的回收。
这就是Reference的一个完整的生命周期。
三个Queue/List
三个Queue/List:ReferenceQueue,discovered List和pending List
。
- ReferenceQueue它本质是由Reference中的next连接而成的。用来存储GC待回收的对象。
- pending List就是待入ReferenceQueue的list。
- discovered List这个有点特别,在Pending状态时候,discovered List就等于pending List。 在Active状态的时候discovered List实际上维持的是一个引用链。通过这个引用链,我们可以获得引用的链式结构,当某个Reference状态不再是Active状态时,需要将这个Reference从discovered List中删除。
总结
Java的四种引用的级别由高到低依次为:强引用 > 软引用 > 弱引用 > 虚引用
回收时机 | 用途 | 生存时间 | |
---|---|---|---|
强 | 重来不会 | 对象的一般状态 | JVM停止运行时终止 |
软 | 内存不足 时 | 联合引用队列构造有效期短、占内存大,生命周期长的对象的二级高速缓冲器(内存不足的时候清空) | 内存不足 时终止 |
弱 | 在垃圾回收时 | 联合引用队列构造有效期短、占内存大,生命周期长的对象的一级高速缓冲器(发生GC的时候清空) | GC运行后终止 |
虚 | 在垃圾回收时 | 联合引用队列来跟踪对象被垃圾回收器回收的活动 | GC运行后终止 |