java引用详解

一般情况下,java开发中用的都是强引用。例如如下代码所示。

1
Object ref = new Object();

实际上在java中,还有一种不太常用的“弱引用”类型。弱引用中分为了软引用(SoftReference),弱引用(WeakReference),虚引用(PhantomReference)。所谓的强引用,其实就是FinalReference。

问题随之而来,为什么引用要分为强弱?弱引用又适用于何种场景?本文结合源码,带大家了解java引用的世界。

引用强弱之分

如果大家对JVM内存回收(GC)有一定了解,一定知道内存回收的前提是对象引用“不可达‘’,才能在回收阶段真正的释放掉对象占用的内存。强引用下,如果对象的引用一直处于可达状态,这块内存是没有办法回收的。在内存资源充沛的情况下还好,但是如果内存资源吃紧,为了服务的可用性,一些“不必要”,或者说“不那么重要的”内存,是不是可以释放掉?这个时候,弱引用就派上了用场。各个引用类的对比,请参考下标

引用类型 回收方式 必须配合ReferenceQueue 说明
FinalReference 不回收 默认情况下使用强引用
SoftReference 内存不充足情况下,GC之后回收,如果使用了queue同WeakReference一样的处理 非必要对象,为了内存空间
WeakReference GC立刻回收,如果使用了queue,需手动把要回收的对象置为null 非必要对象,为了内存空间
PhantomReference(幽灵引用) GC立刻回收 get默认返回null,使用虚引用的目的是通过queue监听对象回收

弱引用典型应用

WeakHashMap,ThreadLocal,JVM缓存实现。

源码分析

下图是java.lang.ref包下的类图,四种引用全部继承自Reference。

img

JDK把引用分为了四个状态。

状态 判定条件 说明 源码注释
Active (queue==ReferenceQueue\ \ ReferenceQueue.NULL) && next == null 新创建的引用对象是这个状态。在GC检测到引用对象已经到达合适的Reachability(可达性)时,GC会根据引用对象在创建时是否指定ReferenceQueue参数进行状态转移,如果指定则转移到pending,否则直接转移到Inactive。 Active: Subject to special treatment by the garbage collector. Some time after the collector detects that the reachability of the referent has changed to the appropriate state, it changes the instance’s state to either Pending or Inactive, depending upon whether or not the instance was registered with a queue when it was created. In the former case it also adds the instance to the pending-Reference list. Newly-created instances are Active.
Pending queue == RefrenceQueue && next == this (jvm设置) pending-Refence链表中引用都是这个状态,它们等着被内部线程ReferenceHandler处理入队(调用RefenceQueue.enqueue方法),没有注册的实例不会进入此状态。 An element of the pending-Reference list, waiting to be enqueued by the Reference-handler thread. Unregistered instances are never in this state.
Enqueued queue == ReferenceQueue.ENQUEUED && next == 下一个要处理的Reference对象,或者链表中最后一个next == this 相应的对象已经为待回收并放到queue中,准备由外部线程来询问queue获取相应的数据。调用ReferenceQueue.enqued方法后的Reference对象处于这个状态,当Reference实例从队列中移除之后,它的状态改为Inactive,没有注册的实例不会进入该状态。 Enqueued: An element of the queue with which the instance was registered when it was created. When an instance is removed from its ReferenceQueue, it is made Inactive. Unregistered instances are never in this state.
Inactive queue == ReferenceQueue.NULL && next == this 即此Reference对象已由外部queue中获取到,并且已经处理掉了。即意味着此对象是可以被回收的,并且对内部封装的对象也可以被回收掉了(具体的回收运行,取决于Clear动作是否被调用,可以理解为进入此状态的Reference对象是应该被回收掉的)一旦Reference对象变成Inactive,它的状态就不会再变化。 Nothing more to do. Once an instance becomes Inactive its state will never change again.

基于上表的讲解,画出了如下引用状态流程图。

img

# java

评论