一.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线程。