小熙前些天看到关于GC回收的一张流程图,查阅之后分享下。
1. 文字叙述:
Java中引用数据类型的引用大致分为四种(JDK 1.2开始),由高到低分别是:强引用、软引用、弱引用、虚引用 (幻想引用)
2. 图释:(这四种引用主要的区别体现在,对象在不同的可达性状态下对垃圾收集的影响)
1. 强引用(strong reference):
(1) 强引用是我们最常见,也是最熟悉的普通对象引用,如创建一个对象引用他(new 对象)。只要处于此种引用,该对象就表示永远不会处于不可及状态,GC就不会回收他,即使JVM内存空间不足,JVM宁愿抛OutOfMemoryError运行时错误(OOM),让程序异常终止,也不会靠回收强引用对象来解决内存不足的问题。
(2)对于一个普通的对象,如果没有其他的引用关系,只要超过了引用的作用域或者显式地将相应(强)引用赋值为null,就意味着此对象可以被垃圾收集了。但要注意的是,并不是赋值为null后就立马被垃圾回收,具体的回收时机还是要看垃圾收集策略的。
示例,在不用对象的时候将引用赋值为 null(ArrayList 的 clear() 方法实现):
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
2. 软引用(soft reference):
(1)软引用在内存足够的时候,GC不会回收它。 只有当JVM认定内存空间不足时才会去回收软引用指向的对象。软引用通常可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,java虚拟机就会把这个软引用加入到与之关联的引用队列中(详见下面的示例)。
(2)JVM会确保在抛出内存溢出(OOM)前清理软引用指向的对象,他会尽可能优先回收长时间闲置不用的软引用指向的对象,对那些刚构建的或刚使用过的软引用指向的对象尽可能的保留。基于软引用的这些特性,软引用可以用来实现很多内存敏感点的缓存场景(如图片缓存和网页缓存),即如果内存还有空闲,可以暂时缓存一些业务场景所需的数据,当内存不足时就可以清理掉,等后面再需要时,可以重新获取并再次缓存。这样就确保在使用缓存提升性能的同时,不会导致耗尽内存。
示例:
// 软应用
// new User 对象,user 是强引用
User user = new User();
// 将 user 强引用转换为 userSoftReference 软引用
SoftReference userSoftReference = new SoftReference<>(user);
// 获取软引用对象,如果被 GC 回收则返回 null
User userSoft = userSoftReference.get();
// 当通过软引用获取的对象为 null 的时候,辅助强应用为 null 可以调用 GC 回收引用
if(userSoft == null) {
user = null;
System.gc();
}
// 是否被 GC 回收,加入到关联队列中
boolean enqueued = userSoftReference.isEnqueued();
3. 弱引用(weak reference):
(1)GC在扫描它所管辖的内存区域时,只要发现弱引用的对象,不管内存空间是否有空闲,都会立刻回收它。 是一种十分临近finalize状态的情况,当弱引用被清除的时候,就符合finalize的条件了。弱引用与软引用最大的区别就是弱引用比软引用的生命周期更短暂。
(2)具体的回收时机还是要看垃圾回收策略的,并且垃圾回收器是一个优先级很低的线程,因此那些弱引用的对象并不是说只要达到弱引用状态就会立马被回收。弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。
示例:
// 弱引用
// new User 对象,user 是强引用
User user = new User();
// 将 user 强引用转换为 userWeakReference 软引用
WeakReference userWeakReference = new WeakReference<>(user);
// 获取弱引用对象,如果被 GC 回收则返回 null
User userWeak = userWeakReference.get();
// 返回是否被 GC 标记为即将回收的垃圾,是否在关联的队列中
boolean enqueued = userWeakReference.isEnqueued();
4. 虚引用(phantom reference):
(1)虚引用在任何时候都可能被GC回收,如果一个对象仅持有虚引用,那么它就和没有任何引用一样,并且虚引用并不会决定对象的生命周期。
(2)虚引用主要用来跟踪对象被垃圾回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列(ReferenceQueue)联合使用,也有人利用虚引用监控对象的创建和销毁。幻象引用的get方法永远返回null,主要用于检查对象是否已经从内测中删除。
示例:
// 虚引用
// new User 对象,user 是强引用
User user = new User();
// 创建必要的关联队列进行存贮,虚引用随时会会被 GC 回收
ReferenceQueue userReferenceQueue = new ReferenceQueue<>();
// 将 user 强引用转换为 userPhantomReference 虚引用
PhantomReference userPhantomReference = new PhantomReference<>(user, userReferenceQueue);
// 任何时候调用都是null
User getUserPhantomReference = userPhantomReference.get();
// 判断是否被内存删除
boolean enqueued = userPhantomReference.isEnqueued();
引用类型 | GC回收时间 | 用处 | 存活时间 |
---|---|---|---|
强引用 | 从来不会被回收 | 一般对象的引用 | JVM停止运行时 |
软引用 | 在内存不足时 | 对象缓存 | 内存不足时 |
弱引用 | 在垃圾回收时 | 对象缓存 | GC回收后 |
虚引用 | 任何时间 | 监控对象的创建和销毁 | Unknow |