之前看过hongjiang对ReferenceQueue的分析
http://hongjiang.info/java-referencequeue/,很赞的分析水准。仔细看完后,总是感觉有几个点还不是非常透彻,现在揣测着补充一下。
一.Reference类的pending成员
pending是由jvm来赋值的,当Reference内部的referent对象的可达状态改变时,jvm会将Reference对象放入pending链表。
实现的逻辑在referenceProcessor.cpp的enqueue_discovered_reflist方法
void ReferenceProcessor::enqueue_discovered_reflist(DiscoveredList& refs_list,
HeapWord* pending_list_addr) {
// Given a list of refs linked through the "discovered" field
// (java.lang.ref.Reference.discovered) chain them through the
// "next" field (java.lang.ref.Reference.next) and prepend
// to the pending list.
if (TraceReferenceGC && PrintGCDetails) {
gclog_or_tty->print_cr("ReferenceProcessor::enqueue_discovered_reflist list "
INTPTR_FORMAT, (address)refs_list.head());
}
oop obj = refs_list.head();
// Walk down the list, copying the discovered field into
// the next field and clearing it (except for the last
// non-sentinel object which is treated specially to avoid
// confusion with an active reference).
while (obj != sentinel_ref()) {
assert(obj->is_instanceRef(), "should be reference object");
oop next = java_lang_ref_Reference::discovered(obj);
if (TraceReferenceGC && PrintGCDetails) {
gclog_or_tty->print_cr(" obj " INTPTR_FORMAT "/next " INTPTR_FORMAT,
obj, next);
}
assert(java_lang_ref_Reference::next(obj) == NULL,
"The reference should not be enqueued");
if (next == sentinel_ref()) { // obj is last
// Swap refs_list into pendling_list_addr and
// set obj's next to what we read from pending_list_addr.
oop old = oopDesc::atomic_exchange_oop(refs_list.head(), pending_list_addr);
// Need oop_check on pending_list_addr above;
// see special oop-check code at the end of
// enqueue_discovered_reflists() further below.
if (old == NULL) {
// obj should be made to point to itself, since
// pending list was empty.
java_lang_ref_Reference::set_next(obj, obj);
} else {
java_lang_ref_Reference::set_next(obj, old);
}
} else {
java_lang_ref_Reference::set_next(obj, next);
}
java_lang_ref_Reference::set_discovered(obj, (oop) NULL);
obj = next;
}
}
不懂C++,但大致从上面的代码可以看出
1.pending实际上指向的是回收列表的header,所以pending属性是static;
2.next指向的是下一个Reference;
3.如果是最后一个节点(哨兵节点),next=自己;
4.设置Reference的discovered为空;
二.Reference类的discovered成员
discovered的赋值是在discover_reference方法中,其中一个分支是调用add_to_discovered_list_mt。
oop retest = oopDesc::atomic_compare_exchange_oop(current_head, discovered_addr,
NULL);
if (retest == NULL) {
// This thread just won the right to enqueue the object.
// We have separate lists for enqueueing so no synchronization
// is necessary.
refs_list.set_head(obj);
refs_list.inc_length(1);
if (_discovered_list_needs_barrier) {
_bs->write_ref_field((void*)discovered_addr, current_head);
}
if (TraceReferenceGC) {
gclog_or_tty->print_cr("Enqueued reference (mt) (" INTPTR_FORMAT ": %s)",
obj, obj->blueprint()->internal_name());
}
} else {
// If retest was non NULL, another thread beat us to it:
// The reference has already been discovered...
if (TraceReferenceGC) {
gclog_or_tty->print_cr("Already enqueued reference (" INTPTR_FORMAT ": %s)",
obj, obj->blueprint()->internal_name());
}
}
1.将refs_list.head设置到discovered;
2.refs_list.set_head(obj),设置自己为refs_list的header,即refs_list的header指向最后回收的Reference;
3.通过步骤1和2实际上也形成了一个链表。这个链表最终会变成pending列表;
三.ReferenceHandler线程唤醒
大致理解了上面的逻辑,再看java.lang.ref.Reference的ReferenceHandler类的run方法就比较好理解了
public void run() {
for (;;) {
Reference r;
synchronized (lock) {
if (pending != null) {
r = pending;
Reference rn = r.next;
pending = (rn == r) ? null :rn;
//java_lang_ref_Reference::set_next(obj, obj), rn == r即最后的节点
r.next = r; //解除链表的连接
} else {
try {
lock.wait();
} catch (InterruptedException x) { }
continue;
}
}
// Fast path for cleaners
if (r instanceof Cleaner) {
((Cleaner)r).clean();
continue;
}
ReferenceQueue q = r.queue;
if (q != ReferenceQueue.NULL)
q.enqueue(r);
}
}
如果pending不为null,则将pending进行enqueue,否则线程进入wait状态。进入wait状态后,ReferenceHandler怎么被唤醒呢?Reference的代码中没有看到唤醒的代码。这个问题也咨询过hongjiang,但没有给出满意的答复。
搜了一下Hotspot的代码,发现一些线索:
在\hotspot\src\share\vm\oops\instanceRefKlass.cpp中
void instanceRefKlass::release_and_notify_pending_list_lock(
BasicLock *pending_list_basic_lock) {
// we may enter this with pending exception set
PRESERVE_EXCEPTION_MARK; // exceptions are never thrown, needed for TRAPS argument
//
Handle h_lock(THREAD, java_lang_ref_Reference::pending_list_lock());
assert(ObjectSynchronizer::current_thread_holds_lock(
JavaThread::current(), h_lock),
"Lock should be held");
// Notify waiters on pending lists lock if there is any reference.
if (java_lang_ref_Reference::pending_list() != NULL) {
ObjectSynchronizer::notifyall(h_lock, THREAD);
}
ObjectSynchronizer::fast_exit(h_lock(), pending_list_basic_lock, THREAD);
if (HAS_PENDING_EXCEPTION) CLEAR_PENDING_EXCEPTION;
}
这一段代码就包含了线程唤醒的操作。
比如在hotspot\src\share\vm\gc_implementation\shared\concurrentGCThread.cpp中,就有对release_and_notify_pending_list_lock的调用
case releaseAndNotifyPLL: {
assert(owned > 0, "Don't have PLL");
instanceRefKlass::release_and_notify_pending_list_lock(&pll_basic_lock);
debug_only(owned--;)
break;
}
大致可以推测在GC时,如果发现java_lang_ref_Reference::pending_list() != NULL,会唤醒ReferenceHandler线程。