目录
1、SoftReference / WeakReference / PhantomReference
4、runFinalization / runAllFinalizers
5、InstanceKlass::register_finalizer
一、Reference
Reference是所有表示对象引用的类的抽象基类,定义了公共的方法,Reference所引用的对象称为referent。因为Reference的实现与JVM的垃圾回收机制是强相关的,所以不建议直接继承Reference,避免改变Reference的关键实现,其类继承关系如下图:
下面逐一讲解各类的使用和源码实现细节。
1、SoftReference / WeakReference / PhantomReference
这三个对象的表示引用是越来越弱的,SoftReference通常用来实现内存敏感的缓存,当内存不足的时候,为了获取可用内存空间会回收SoftReference所引用的对象,SoftReference本身会被放到创建时传入的ReferenceQueue中,JVM保证在抛出OutOfMemoryError异常前,清除所有的SoftReference。SoftReference增加了两个属性:
- static long clock; //由垃圾回收器维护的表示时间的字段
- long timestamp; //用来保存当前的clock
SoftReference改写了原来的构造方法和get方法的实现,增加了timestamp属性的更新逻辑,如下:
public SoftReference(T referent) {
super(referent);
this.timestamp = clock;
}
public SoftReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
this.timestamp = clock;
}
public T get() {
T o = super.get();
if (o != null && this.timestamp != clock)
this.timestamp = clock;
return o;
}
WeakReference通常用来实现类似WeakMap的特殊Map,不能阻止key或者value被垃圾回收了,当垃圾回收器发现一个对象只是被WeakReference所引用就会回收掉该对象,并将关联的WeakReference加入到创建时传入的ReferenceQueue中。WeakReference没有新增属性,只是定义了自己的构造方法而已,如下:
public WeakReference(T referent) {
super(referent);
}
public WeakReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
}
PhantomReference通常用来实现一种更流畅的类似Object.finalize的清理功能,与SoftReference和WeakReference不同的是,PhantomReference的get方法永远返回null,为了保证其所引用的对象一直处于可被回收的状态,并且当垃圾回收器判断某个对象只是被PhantomReference所引用,然后将PhantomReference加入到创建时传入的ReferenceQueue中,这个时候垃圾回收器不会立即回收掉PhantomReference所引用的对象,而是等到所有的PhantomReference对象都放到ReferenceQueue中或者PhantomReference对象本身变得不可达。WeakReference也没有新增属性,改写了原有的get方法实现,永远返回null,如下:
public T get() {
return null;
}
public PhantomReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
}
这三个类的代码调用都大同小异,具体可以参考《java8 WeakHashMap接口实现源码解析》中WeakReference的应用。
2、定义
Reference包含的属性如下:
- T referent; //所引用的对象
- volatile ReferenceQueue<? super T> queue; //当所引用的对象referent被清理时用来保存Reference的队列,调用方可以通过ReferenceQueue的poll方法获取队列中的Reference实例,从而知道哪些referent对象被回收掉了
- volatile Reference next; //ReferenceQueue使用,通过next属性将所有加入到ReferenceQueue中的Reference实例构成一个链表
- transient Reference<T> discovered; //JVM使用的,用于将所有的Pending状态的Reference实例构成一个链表
- static Lock lock = new Lock(); //用来修改Pending状态的Reference实例链表的锁
- static Reference<Object> pending = null; //Pending状态的Reference实例链表的链表头元素,垃圾回收器发现某个对象只有Reference实例引用,就会把Reference对象加入到这个链表中,而ReferenceHandler Thread不断从这个链表中移除元素,将其加入到Reference实例创建时传入的ReferenceQueue队列中
Reference定义了四种内部状态:
- Active 新创建的Reference实例就是Active状态,当垃圾回收器发现所引用的对象referent的可达状态发生变更了,可能将Reference实例的状态改成Pending或者Inactive,取决于Reference实例创建时是否传入ReferenceQueue实例引用。如果是从Active改成Pending,则垃圾回收器还会将该Reference实例加入到pending属性对应的Reference列表中
- Pending pending属性对应的Reference列表中的Reference实例的状态都是Pending,等待ReferenceHandler Thread将这些实例加入到queue队列中
- Enqueued queue属性对应的ReferenceQueue队列中的Reference实例的状态都是Enqueued,当实例从ReferenceQueue队列中移除就会变成Inactive。如果Reference实例在创建时没有传入ReferenceQueue,则永远不会处于Enqueued状态。
- Inactive 变成Inactive以后,状态就不会再变更,等待垃圾回收器回收掉该实例
在不同的状态下,queue和next属性的赋值会发生变更,如下:
- Active queue就是Reference实例创建时传入的ReferenceQueue引用,如果没有传入或者传入的是null,则为ReferenceQueue.NULL;此时next属性为null。
- Pending queue就是Reference实例创建时传入的ReferenceQueue引用,next属性是this
- Enqueued queue就是ReferenceQueue.ENQUEUED,next属性就是队列中的下一个元素,如果当前Reference实例就是最后一个,则是this
- Inactive queue就是ReferenceQueue.NULL,next属性就是this
Reference定义的方法比较简单,如下:
其中get方法返回所引用的对象referent,clear方法用于将referent置为null,enqueue方法用于将当前Reference实例加入到创建时传入的queue队列中,isEnqueued方法判断当前Reference实例是否已加入queue队列中,tryHandlePending方法是ReferenceHandler Thread调用的用于处理Pending状态的Reference实例的核心方法,是包级访问的。重点关注ReferenceHandler的实现。
3、ReferenceHandler
ReferenceHandler继承自Thread,表示一个不断将Pending状态的Reference实例放入该实例创建时传入的ReferenceQueue实例中,所有处于Pending状态的Reference实例通过discovered实例属性构成链表,链表头就是Reference类的静态属性pending,在遍历链表时,如果链表为空则通过lock.wait()的方式等待;如果遍历的Reference实例是Cleaner,则调用其clean方法,用于清理资源清理。其实现如下:
private static class ReferenceHandler extends Thread {
private static void ensureClassInitialized(Class<?> clazz) {
try {
Class.forName(clazz.getName(), true, clazz.getClassLoader());
} catch (ClassNotFoundException e) {
throw (Error) new NoClassDefFoundError(e.getMessage()).initCause(e);
}
}
static {
//预先加载并初始化两个类
ensureClassInitialized(InterruptedException.class);
ensureClassInitialized(Cleaner.class);
}
ReferenceHandler(ThreadGroup g, String name) {
super(g, name);
}
public void run() {
while (true) {
//while true不断执行
tryHandlePending(true);
}
}
}
static boolean tryHandlePending(boolean waitForNotify) {
Reference<Object> r;
Cleaner c;
try {
//注意lock和pending都是静态属性
synchronized (lock) {
if (pending != null) {
//如果存在待处理的Reference实例
r = pending