8203028: Simplify reference processing in light of JDK-8175797

Removed special handling of Reference.next

Reviewed-by: tschatzl, sjohanss, mchung
This commit is contained in:
Kim Barrett 2018-05-26 03:11:50 -04:00
parent ace1b8a4c9
commit 6c20824cda
9 changed files with 197 additions and 210 deletions

View file

@ -200,16 +200,9 @@ static void oop_pc_follow_contents_specialized(InstanceRefKlass* klass, oop obj,
cm->mark_and_push(referent_addr); cm->mark_and_push(referent_addr);
} }
} }
T* next_addr = (T*)java_lang_ref_Reference::next_addr_raw(obj); // Treat discovered as normal oop.
// Treat discovered as normal oop, if ref is not "active", T* discovered_addr = (T*)java_lang_ref_Reference::discovered_addr_raw(obj);
// i.e. if next is non-NULL. cm->mark_and_push(discovered_addr);
T next_oop = RawAccess<>::oop_load(next_addr);
if (!CompressedOops::is_null(next_oop)) { // i.e. ref is not "active"
T* discovered_addr = (T*)java_lang_ref_Reference::discovered_addr_raw(obj);
log_develop_trace(gc, ref)(" Process discovered as normal " PTR_FORMAT, p2i(discovered_addr));
cm->mark_and_push(discovered_addr);
}
cm->mark_and_push(next_addr);
klass->InstanceKlass::oop_pc_follow_contents(obj, cm); klass->InstanceKlass::oop_pc_follow_contents(obj, cm);
} }

View file

@ -3081,13 +3081,10 @@ void InstanceClassLoaderKlass::oop_pc_update_pointers(oop obj, ParCompactionMana
#ifdef ASSERT #ifdef ASSERT
template <class T> static void trace_reference_gc(const char *s, oop obj, template <class T> static void trace_reference_gc(const char *s, oop obj,
T* referent_addr, T* referent_addr,
T* next_addr,
T* discovered_addr) { T* discovered_addr) {
log_develop_trace(gc, ref)("%s obj " PTR_FORMAT, s, p2i(obj)); log_develop_trace(gc, ref)("%s obj " PTR_FORMAT, s, p2i(obj));
log_develop_trace(gc, ref)(" referent_addr/* " PTR_FORMAT " / " PTR_FORMAT, log_develop_trace(gc, ref)(" referent_addr/* " PTR_FORMAT " / " PTR_FORMAT,
p2i(referent_addr), referent_addr ? p2i((oop)RawAccess<>::oop_load(referent_addr)) : NULL); p2i(referent_addr), referent_addr ? p2i((oop)RawAccess<>::oop_load(referent_addr)) : NULL);
log_develop_trace(gc, ref)(" next_addr/* " PTR_FORMAT " / " PTR_FORMAT,
p2i(next_addr), next_addr ? p2i((oop)RawAccess<>::oop_load(next_addr)) : NULL);
log_develop_trace(gc, ref)(" discovered_addr/* " PTR_FORMAT " / " PTR_FORMAT, log_develop_trace(gc, ref)(" discovered_addr/* " PTR_FORMAT " / " PTR_FORMAT,
p2i(discovered_addr), discovered_addr ? p2i((oop)RawAccess<>::oop_load(discovered_addr)) : NULL); p2i(discovered_addr), discovered_addr ? p2i((oop)RawAccess<>::oop_load(discovered_addr)) : NULL);
} }
@ -3097,12 +3094,10 @@ template <class T>
static void oop_pc_update_pointers_specialized(oop obj, ParCompactionManager* cm) { static void oop_pc_update_pointers_specialized(oop obj, ParCompactionManager* cm) {
T* referent_addr = (T*)java_lang_ref_Reference::referent_addr_raw(obj); T* referent_addr = (T*)java_lang_ref_Reference::referent_addr_raw(obj);
PSParallelCompact::adjust_pointer(referent_addr, cm); PSParallelCompact::adjust_pointer(referent_addr, cm);
T* next_addr = (T*)java_lang_ref_Reference::next_addr_raw(obj);
PSParallelCompact::adjust_pointer(next_addr, cm);
T* discovered_addr = (T*)java_lang_ref_Reference::discovered_addr_raw(obj); T* discovered_addr = (T*)java_lang_ref_Reference::discovered_addr_raw(obj);
PSParallelCompact::adjust_pointer(discovered_addr, cm); PSParallelCompact::adjust_pointer(discovered_addr, cm);
debug_only(trace_reference_gc("InstanceRefKlass::oop_update_ptrs", obj, debug_only(trace_reference_gc("InstanceRefKlass::oop_update_ptrs", obj,
referent_addr, next_addr, discovered_addr);) referent_addr, discovered_addr);)
} }
void InstanceRefKlass::oop_pc_update_pointers(oop obj, ParCompactionManager* cm) { void InstanceRefKlass::oop_pc_update_pointers(oop obj, ParCompactionManager* cm) {

View file

@ -442,7 +442,7 @@ static void oop_ps_push_contents_specialized(oop obj, InstanceRefKlass *klass, P
if (PSScavenge::should_scavenge(referent_addr)) { if (PSScavenge::should_scavenge(referent_addr)) {
ReferenceProcessor* rp = PSScavenge::reference_processor(); ReferenceProcessor* rp = PSScavenge::reference_processor();
if (rp->discover_reference(obj, klass->reference_type())) { if (rp->discover_reference(obj, klass->reference_type())) {
// reference already enqueued, referent and next will be traversed later // reference discovered, referent will be traversed later.
klass->InstanceKlass::oop_ps_push_contents(obj, pm); klass->InstanceKlass::oop_ps_push_contents(obj, pm);
return; return;
} else { } else {
@ -450,20 +450,10 @@ static void oop_ps_push_contents_specialized(oop obj, InstanceRefKlass *klass, P
pm->claim_or_forward_depth(referent_addr); pm->claim_or_forward_depth(referent_addr);
} }
} }
// Treat discovered as normal oop, if ref is not "active", // Treat discovered as normal oop
// i.e. if next is non-NULL. T* discovered_addr = (T*)java_lang_ref_Reference::discovered_addr_raw(obj);
T* next_addr = (T*)java_lang_ref_Reference::next_addr_raw(obj); if (PSScavenge::should_scavenge(discovered_addr)) {
T next_oop = RawAccess<>::oop_load(next_addr); pm->claim_or_forward_depth(discovered_addr);
if (!CompressedOops::is_null(next_oop)) { // i.e. ref is not "active"
T* discovered_addr = (T*)java_lang_ref_Reference::discovered_addr_raw(obj);
log_develop_trace(gc, ref)(" Process discovered as normal " PTR_FORMAT, p2i(discovered_addr));
if (PSScavenge::should_scavenge(discovered_addr)) {
pm->claim_or_forward_depth(discovered_addr);
}
}
// Treat next as normal oop; next is a link in the reference queue.
if (PSScavenge::should_scavenge(next_addr)) {
pm->claim_or_forward_depth(next_addr);
} }
klass->InstanceKlass::oop_ps_push_contents(obj, pm); klass->InstanceKlass::oop_ps_push_contents(obj, pm);
} }

View file

@ -303,9 +303,6 @@ void DiscoveredListIterator::clear_referent() {
} }
void DiscoveredListIterator::enqueue() { void DiscoveredListIterator::enqueue() {
// Self-loop next, so as to make Ref not active.
java_lang_ref_Reference::set_next_raw(_current_discovered, _current_discovered);
HeapAccess<AS_NO_KEEPALIVE>::oop_store_at(_current_discovered, HeapAccess<AS_NO_KEEPALIVE>::oop_store_at(_current_discovered,
java_lang_ref_Reference::discovered_offset, java_lang_ref_Reference::discovered_offset,
_next_discovered); _next_discovered);
@ -364,38 +361,35 @@ ReferenceProcessor::process_phase1(DiscoveredList& refs_list,
iter.removed(), iter.processed(), p2i(&refs_list)); iter.removed(), iter.processed(), p2i(&refs_list));
} }
inline void log_dropped_ref(const DiscoveredListIterator& iter, const char* reason) {
log_develop_trace(gc, ref)("Dropping %s reference " PTR_FORMAT ": %s",
reason, p2i(iter.obj()),
iter.obj()->klass()->internal_name());
}
// Traverse the list and remove any Refs whose referents are alive,
// or NULL if discovery is not atomic.
void ReferenceProcessor::process_phase2(DiscoveredList& refs_list, void ReferenceProcessor::process_phase2(DiscoveredList& refs_list,
BoolObjectClosure* is_alive, BoolObjectClosure* is_alive,
OopClosure* keep_alive, OopClosure* keep_alive,
VoidClosure* complete_gc) { VoidClosure* complete_gc) {
if (discovery_is_atomic()) { // complete_gc is unused.
// complete_gc is ignored in this case for this phase
pp2_work(refs_list, is_alive, keep_alive);
} else {
assert(complete_gc != NULL, "Error");
pp2_work_concurrent_discovery(refs_list, is_alive,
keep_alive, complete_gc);
}
}
// Traverse the list and remove any Refs that are not active, or
// whose referents are either alive or NULL.
void
ReferenceProcessor::pp2_work(DiscoveredList& refs_list,
BoolObjectClosure* is_alive,
OopClosure* keep_alive) {
assert(discovery_is_atomic(), "Error");
DiscoveredListIterator iter(refs_list, keep_alive, is_alive); DiscoveredListIterator iter(refs_list, keep_alive, is_alive);
while (iter.has_next()) { while (iter.has_next()) {
iter.load_ptrs(DEBUG_ONLY(false /* allow_null_referent */)); iter.load_ptrs(DEBUG_ONLY(!discovery_is_atomic() /* allow_null_referent */));
DEBUG_ONLY(oop next = java_lang_ref_Reference::next(iter.obj());) if (iter.referent() == NULL) {
assert(next == NULL, "Should not discover inactive Reference"); // Reference has been cleared since discovery; only possible if
if (iter.is_referent_alive()) { // discovery is not atomic (checked by load_ptrs). Remove
log_develop_trace(gc, ref)("Dropping strongly reachable reference (" INTPTR_FORMAT ": %s)", // reference from list.
p2i(iter.obj()), iter.obj()->klass()->internal_name()); log_dropped_ref(iter, "cleared");
// The referent is reachable after all.
// Remove Reference object from list.
iter.remove(); iter.remove();
// Update the referent pointer as necessary: Note that this iter.move_to_next();
} else if (iter.is_referent_alive()) {
// The referent is reachable after all.
// Remove reference from list.
log_dropped_ref(iter, "reachable");
iter.remove();
// Update the referent pointer as necessary. Note that this
// should not entail any recursive marking because the // should not entail any recursive marking because the
// referent must already have been traversed. // referent must already have been traversed.
iter.make_referent_alive(); iter.make_referent_alive();
@ -413,45 +407,6 @@ ReferenceProcessor::pp2_work(DiscoveredList& refs_list,
) )
} }
void
ReferenceProcessor::pp2_work_concurrent_discovery(DiscoveredList& refs_list,
BoolObjectClosure* is_alive,
OopClosure* keep_alive,
VoidClosure* complete_gc) {
assert(!discovery_is_atomic(), "Error");
DiscoveredListIterator iter(refs_list, keep_alive, is_alive);
while (iter.has_next()) {
iter.load_ptrs(DEBUG_ONLY(true /* allow_null_referent */));
HeapWord* next_addr = java_lang_ref_Reference::next_addr_raw(iter.obj());
oop next = java_lang_ref_Reference::next(iter.obj());
if ((iter.referent() == NULL || iter.is_referent_alive() ||
next != NULL)) {
assert(oopDesc::is_oop_or_null(next), "Expected an oop or NULL for next field at " PTR_FORMAT, p2i(next));
// Remove Reference object from list
iter.remove();
// Trace the cohorts
iter.make_referent_alive();
if (UseCompressedOops) {
keep_alive->do_oop((narrowOop*)next_addr);
} else {
keep_alive->do_oop((oop*)next_addr);
}
iter.move_to_next();
} else {
iter.next();
}
}
// Now close the newly reachable set
complete_gc->do_void();
NOT_PRODUCT(
if (iter.processed() > 0) {
log_develop_trace(gc, ref)(" Dropped " SIZE_FORMAT " active Refs out of " SIZE_FORMAT
" Refs in discovered list " INTPTR_FORMAT,
iter.removed(), iter.processed(), p2i(&refs_list));
}
)
}
void ReferenceProcessor::process_phase3(DiscoveredList& refs_list, void ReferenceProcessor::process_phase3(DiscoveredList& refs_list,
bool clear_referent, bool clear_referent,
BoolObjectClosure* is_alive, BoolObjectClosure* is_alive,
@ -465,8 +420,12 @@ void ReferenceProcessor::process_phase3(DiscoveredList& refs_list,
// NULL out referent pointer // NULL out referent pointer
iter.clear_referent(); iter.clear_referent();
} else { } else {
// keep the referent around // Current reference is a FinalReference; that's the only kind we
// don't clear the referent, instead keeping it for calling finalize.
iter.make_referent_alive(); iter.make_referent_alive();
// Self-loop next, to mark it not active.
assert(java_lang_ref_Reference::next(iter.obj()) == NULL, "enqueued FinalReference");
java_lang_ref_Reference::set_next_raw(iter.obj(), iter.obj());
} }
iter.enqueue(); iter.enqueue();
log_develop_trace(gc, ref)("Adding %sreference (" INTPTR_FORMAT ": %s) as pending", log_develop_trace(gc, ref)("Adding %sreference (" INTPTR_FORMAT ": %s) as pending",
@ -913,9 +872,9 @@ bool ReferenceProcessor::discover_reference(oop obj, ReferenceType rt) {
if (!_discovering_refs || !RegisterReferences) { if (!_discovering_refs || !RegisterReferences) {
return false; return false;
} }
// We only discover active references.
oop next = java_lang_ref_Reference::next(obj); if ((rt == REF_FINAL) && (java_lang_ref_Reference::next(obj) != NULL)) {
if (next != NULL) { // Ref is no longer active // Don't rediscover non-active FinalReferences.
return false; return false;
} }
@ -1121,24 +1080,15 @@ bool ReferenceProcessor::preclean_discovered_reflist(DiscoveredList& refs_lis
return true; return true;
} }
iter.load_ptrs(DEBUG_ONLY(true /* allow_null_referent */)); iter.load_ptrs(DEBUG_ONLY(true /* allow_null_referent */));
oop obj = iter.obj(); if (iter.referent() == NULL || iter.is_referent_alive()) {
oop next = java_lang_ref_Reference::next(obj); // The referent has been cleared, or is alive; we need to trace
if (iter.referent() == NULL || iter.is_referent_alive() || next != NULL) { // and mark its cohort.
// The referent has been cleared, or is alive, or the Reference is not
// active; we need to trace and mark its cohort.
log_develop_trace(gc, ref)("Precleaning Reference (" INTPTR_FORMAT ": %s)", log_develop_trace(gc, ref)("Precleaning Reference (" INTPTR_FORMAT ": %s)",
p2i(iter.obj()), iter.obj()->klass()->internal_name()); p2i(iter.obj()), iter.obj()->klass()->internal_name());
// Remove Reference object from list // Remove Reference object from list
iter.remove(); iter.remove();
// Keep alive its cohort. // Keep alive its cohort.
iter.make_referent_alive(); iter.make_referent_alive();
if (UseCompressedOops) {
narrowOop* next_addr = (narrowOop*)java_lang_ref_Reference::next_addr_raw(obj);
keep_alive->do_oop(next_addr);
} else {
oop* next_addr = (oop*)java_lang_ref_Reference::next_addr_raw(obj);
keep_alive->do_oop(next_addr);
}
iter.move_to_next(); iter.move_to_next();
} else { } else {
iter.next(); iter.next();

View file

@ -262,15 +262,6 @@ class ReferenceProcessor : public ReferenceDiscoverer {
BoolObjectClosure* is_alive, BoolObjectClosure* is_alive,
OopClosure* keep_alive, OopClosure* keep_alive,
VoidClosure* complete_gc); VoidClosure* complete_gc);
// Work methods in support of process_phase2
void pp2_work(DiscoveredList& refs_list,
BoolObjectClosure* is_alive,
OopClosure* keep_alive);
void pp2_work_concurrent_discovery(
DiscoveredList& refs_list,
BoolObjectClosure* is_alive,
OopClosure* keep_alive,
VoidClosure* complete_gc);
// Phase3: process the referents by either clearing them // Phase3: process the referents by either clearing them
// or keeping them alive (and their closure), and enqueuing them. // or keeping them alive (and their closure), and enqueuing them.
void process_phase3(DiscoveredList& refs_list, void process_phase3(DiscoveredList& refs_list,

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -30,10 +30,8 @@
void InstanceRefKlass::update_nonstatic_oop_maps(Klass* k) { void InstanceRefKlass::update_nonstatic_oop_maps(Klass* k) {
// Clear the nonstatic oop-map entries corresponding to referent // Clear the nonstatic oop-map entries corresponding to referent
// and nextPending field. They are treated specially by the // and discovered fields. They are treated specially by the
// garbage collector. // garbage collector.
// The discovered field is used only by the garbage collector
// and is also treated specially.
InstanceKlass* ik = InstanceKlass::cast(k); InstanceKlass* ik = InstanceKlass::cast(k);
// Check that we have the right class // Check that we have the right class
@ -45,22 +43,33 @@ void InstanceRefKlass::update_nonstatic_oop_maps(Klass* k) {
OopMapBlock* map = ik->start_of_nonstatic_oop_maps(); OopMapBlock* map = ik->start_of_nonstatic_oop_maps();
// Check that the current map is (2,4) - currently points at field with #ifdef ASSERT
// offset 2 (words) and has 4 map entries. // Verify fields are in the expected places.
debug_only(int offset = java_lang_ref_Reference::referent_offset); int referent_offset = java_lang_ref_Reference::referent_offset;
debug_only(unsigned int count = ((java_lang_ref_Reference::discovered_offset - int queue_offset = java_lang_ref_Reference::queue_offset;
java_lang_ref_Reference::referent_offset)/heapOopSize) + 1); int next_offset = java_lang_ref_Reference::next_offset;
int discovered_offset = java_lang_ref_Reference::discovered_offset;
assert(referent_offset < queue_offset, "just checking");
assert(queue_offset < next_offset, "just checking");
assert(next_offset < discovered_offset, "just checking");
const unsigned int count =
1 + ((discovered_offset - referent_offset) / heapOopSize);
assert(count == 4, "just checking");
#endif // ASSERT
// Updated map starts at "queue", covers "queue" and "next".
const int new_offset = java_lang_ref_Reference::queue_offset;
const unsigned int new_count = 2; // queue and next
// Verify existing map is as expected, and update if needed.
if (UseSharedSpaces) { if (UseSharedSpaces) {
assert(map->offset() == java_lang_ref_Reference::queue_offset && assert(map->offset() == new_offset, "just checking");
map->count() == 1, "just checking"); assert(map->count() == new_count, "just checking");
} else { } else {
assert(map->offset() == offset && map->count() == count, assert(map->offset() == referent_offset, "just checking");
"just checking"); assert(map->count() == count, "just checking");
map->set_offset(new_offset);
// Update map to (3,1) - point to offset of 3 (words) with 1 map entry. map->set_count(new_count);
map->set_offset(java_lang_ref_Reference::queue_offset);
map->set_count(1);
} }
} }
@ -74,7 +83,7 @@ void InstanceRefKlass::oop_verify_on(oop obj, outputStream* st) {
if (referent != NULL) { if (referent != NULL) {
guarantee(oopDesc::is_oop(referent), "referent field heap failed"); guarantee(oopDesc::is_oop(referent), "referent field heap failed");
} }
// Verify next field // Additional verification for next field, which must be a Reference or null
oop next = java_lang_ref_Reference::next(obj); oop next = java_lang_ref_Reference::next(obj);
if (next != NULL) { if (next != NULL) {
guarantee(oopDesc::is_oop(next), "next field should be an oop"); guarantee(oopDesc::is_oop(next), "next field should be an oop");

View file

@ -45,14 +45,6 @@ void InstanceRefKlass::do_referent(oop obj, OopClosureType* closure, Contains& c
} }
} }
template <bool nv, typename T, class OopClosureType, class Contains>
void InstanceRefKlass::do_next(oop obj, OopClosureType* closure, Contains& contains) {
T* next_addr = (T*)java_lang_ref_Reference::next_addr_raw(obj);
if (contains(next_addr)) {
Devirtualizer<nv>::do_oop(closure, next_addr);
}
}
template <bool nv, typename T, class OopClosureType, class Contains> template <bool nv, typename T, class OopClosureType, class Contains>
void InstanceRefKlass::do_discovered(oop obj, OopClosureType* closure, Contains& contains) { void InstanceRefKlass::do_discovered(oop obj, OopClosureType* closure, Contains& contains) {
T* discovered_addr = (T*)java_lang_ref_Reference::discovered_addr_raw(obj); T* discovered_addr = (T*)java_lang_ref_Reference::discovered_addr_raw(obj);
@ -84,24 +76,15 @@ void InstanceRefKlass::oop_oop_iterate_discovery(oop obj, ReferenceType type, Oo
return; return;
} }
// Treat referent as normal oop. // Treat referent and discovered as normal oops.
do_referent<nv, T>(obj, closure, contains); do_referent<nv, T>(obj, closure, contains);
do_discovered<nv, T>(obj, closure, contains);
// Treat discovered as normal oop, if ref is not "active" (next non-NULL).
T next_oop = RawAccess<>::oop_load((T*)java_lang_ref_Reference::next_addr_raw(obj));
if (!CompressedOops::is_null(next_oop)) {
do_discovered<nv, T>(obj, closure, contains);
}
// Treat next as normal oop.
do_next<nv, T>(obj, closure, contains);
} }
template <bool nv, typename T, class OopClosureType, class Contains> template <bool nv, typename T, class OopClosureType, class Contains>
void InstanceRefKlass::oop_oop_iterate_fields(oop obj, OopClosureType* closure, Contains& contains) { void InstanceRefKlass::oop_oop_iterate_fields(oop obj, OopClosureType* closure, Contains& contains) {
do_referent<nv, T>(obj, closure, contains); do_referent<nv, T>(obj, closure, contains);
do_discovered<nv, T>(obj, closure, contains); do_discovered<nv, T>(obj, closure, contains);
do_next<nv, T>(obj, closure, contains);
} }
template <bool nv, typename T, class OopClosureType, class Contains> template <bool nv, typename T, class OopClosureType, class Contains>
@ -192,14 +175,11 @@ void InstanceRefKlass::oop_oop_iterate_bounded(oop obj, OopClosureType* closure,
template <typename T> template <typename T>
void InstanceRefKlass::trace_reference_gc(const char *s, oop obj) { void InstanceRefKlass::trace_reference_gc(const char *s, oop obj) {
T* referent_addr = (T*) java_lang_ref_Reference::referent_addr_raw(obj); T* referent_addr = (T*) java_lang_ref_Reference::referent_addr_raw(obj);
T* next_addr = (T*) java_lang_ref_Reference::next_addr_raw(obj);
T* discovered_addr = (T*) java_lang_ref_Reference::discovered_addr_raw(obj); T* discovered_addr = (T*) java_lang_ref_Reference::discovered_addr_raw(obj);
log_develop_trace(gc, ref)("InstanceRefKlass %s for obj " PTR_FORMAT, s, p2i(obj)); log_develop_trace(gc, ref)("InstanceRefKlass %s for obj " PTR_FORMAT, s, p2i(obj));
log_develop_trace(gc, ref)(" referent_addr/* " PTR_FORMAT " / " PTR_FORMAT, log_develop_trace(gc, ref)(" referent_addr/* " PTR_FORMAT " / " PTR_FORMAT,
p2i(referent_addr), p2i(referent_addr ? RawAccess<>::oop_load(referent_addr) : (oop)NULL)); p2i(referent_addr), p2i(referent_addr ? RawAccess<>::oop_load(referent_addr) : (oop)NULL));
log_develop_trace(gc, ref)(" next_addr/* " PTR_FORMAT " / " PTR_FORMAT,
p2i(next_addr), p2i(next_addr ? RawAccess<>::oop_load(next_addr) : (oop)NULL));
log_develop_trace(gc, ref)(" discovered_addr/* " PTR_FORMAT " / " PTR_FORMAT, log_develop_trace(gc, ref)(" discovered_addr/* " PTR_FORMAT " / " PTR_FORMAT,
p2i(discovered_addr), p2i(discovered_addr ? RawAccess<>::oop_load(discovered_addr) : (oop)NULL)); p2i(discovered_addr), p2i(discovered_addr ? RawAccess<>::oop_load(discovered_addr) : (oop)NULL));
} }

View file

@ -43,71 +43,146 @@ import jdk.internal.ref.Cleaner;
public abstract class Reference<T> { public abstract class Reference<T> {
/* A Reference instance is in one of four possible internal states: /* The state of a Reference object is characterized by two attributes. It
* may be either "active", "pending", or "inactive". It may also be
* either "registered", "enqueued", "dequeued", or "unregistered".
* *
* Active: Subject to special treatment by the garbage collector. Some * Active: Subject to special treatment by the garbage collector. Some
* time after the collector detects that the reachability of the * time after the collector detects that the reachability of the
* referent has changed to the appropriate state, it changes the * referent has changed to the appropriate state, the collector
* instance's state to either Pending or Inactive, depending upon * "notifies" the reference, changing the state to either "pending" or
* whether or not the instance was registered with a queue when it was * "inactive".
* created. In the former case it also adds the instance to the * referent != null; discovered = null, or in GC discovered list.
* pending-Reference list. Newly-created instances are Active.
* *
* Pending: An element of the pending-Reference list, waiting to be * Pending: An element of the pending-Reference list, waiting to be
* enqueued by the Reference-handler thread. Unregistered instances * processed by the ReferenceHandler thread. The pending-Reference
* are never in this state. * list is linked through the discovered fields of references in the
* list.
* referent = null; discovered = next element in pending-Reference list.
* *
* Enqueued: An element of the queue with which the instance was * Inactive: Neither Active nor Pending.
* registered when it was created. When an instance is removed from * referent = null.
* its ReferenceQueue, it is made Inactive. Unregistered instances are
* never in this state.
* *
* Inactive: Nothing more to do. Once an instance becomes Inactive its * Registered: Associated with a queue when created, and not yet added
* state will never change again. * to the queue.
* queue = the associated queue.
* *
* The state is encoded in the queue and next fields as follows: * Enqueued: Added to the associated queue, and not yet removed.
* queue = ReferenceQueue.ENQUEUE; next = next entry in list, or this to
* indicate end of list.
* *
* Active: queue = ReferenceQueue with which instance is registered, or * Dequeued: Added to the associated queue and then removed.
* ReferenceQueue.NULL if it was not registered with a queue; next = * queue = ReferenceQueue.NULL; next = this.
* null.
* *
* Pending: queue = ReferenceQueue with which instance is registered; * Unregistered: Not associated with a queue when created.
* next = this * queue = ReferenceQueue.NULL.
* *
* Enqueued: queue = ReferenceQueue.ENQUEUED; next = Following instance * The collector only needs to examine the referent field and the
* in queue, or this if at end of list. * discovered field to determine whether a (non-FinalReference) Reference
* object needs special treatment. If the referent is non-null and not
* known to be live, then it may need to be discovered for possible later
* notification. But if the discovered field is non-null, then it has
* already been discovered.
* *
* Inactive: queue = ReferenceQueue.NULL; next = this. * FinalReference (which exists to support finalization) differs from
* other references, because a FinalReference is not cleared when
* notified. The referent being null or not cannot be used to distinguish
* between the active state and pending or inactive states. However,
* FinalReferences do not support enqueue(). Instead, the next field of a
* FinalReference object is set to "this" when it is added to the
* pending-Reference list. The use of "this" as the value of next in the
* enqueued and dequeued states maintains the non-active state. An
* additional check that the next field is null is required to determine
* that a FinalReference object is active.
* *
* With this scheme the collector need only examine the next field in order * Initial states:
* to determine whether a Reference instance requires special treatment: If * [active/registered]
* the next field is null then the instance is active; if it is non-null, * [active/unregistered] [1]
* then the collector should treat the instance normally.
* *
* To ensure that a concurrent collector can discover active Reference * Transitions:
* objects without interfering with application threads that may apply * clear
* the enqueue() method to those objects, collectors should link * [active/registered] -------> [inactive/registered]
* discovered objects through the discovered field. The discovered * | |
* field is also used for linking Reference objects in the pending list. * | | enqueue [2]
* | GC enqueue [2] |
* | -----------------|
* | |
* v |
* [pending/registered] --- v
* | | ReferenceHandler
* | enqueue [2] |---> [inactive/enqueued]
* v | |
* [pending/enqueued] --- |
* | | poll/remove
* | poll/remove |
* | |
* v ReferenceHandler v
* [pending/dequeued] ------> [inactive/dequeued]
*
*
* clear/enqueue/GC [3]
* [active/unregistered] ------
* | |
* | GC |
* | |--> [inactive/unregistered]
* v |
* [pending/unregistered] ------
* ReferenceHandler
*
* Terminal states:
* [inactive/dequeued]
* [inactive/unregistered]
*
* Unreachable states (because enqueue also clears):
* [active/enqeued]
* [active/dequeued]
*
* [1] Unregistered is not permitted for FinalReferences.
*
* [2] These transitions are not possible for FinalReferences, making
* [pending/enqueued] and [pending/dequeued] unreachable, and
* [inactive/registered] terminal.
*
* [3] The garbage collector may directly transition a Reference
* from [active/unregistered] to [inactive/unregistered],
* bypassing the pending-Reference list.
*/ */
private T referent; /* Treated specially by GC */ private T referent; /* Treated specially by GC */
/* The queue this reference gets enqueued to by GC notification or by
* calling enqueue().
*
* When registered: the queue with which this reference is registered.
* enqueued: ReferenceQueue.ENQUEUE
* dequeued: ReferenceQueue.NULL
* unregistered: ReferenceQueue.NULL
*/
volatile ReferenceQueue<? super T> queue; volatile ReferenceQueue<? super T> queue;
/* When active: NULL /* The link in a ReferenceQueue's list of Reference objects.
* pending: this *
* Enqueued: next reference in queue (or this if last) * When registered: null
* Inactive: this * enqueued: next element in queue (or this if last)
* dequeued: this (marking FinalReferences as inactive)
* unregistered: null
*/ */
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
volatile Reference next; volatile Reference next;
/* When active: next element in a discovered reference list maintained by GC (or this if last) /* Used by the garbage collector to accumulate Reference objects that need
* pending: next element in the pending list (or null if last) * to be revisited in order to decide whether they should be notified.
* otherwise: NULL * Also used as the link in the pending-Reference list. The discovered
* field and the next field are distinct to allow the enqueue() method to
* be applied to a Reference object while it is either in the
* pending-Reference list or in the garbage collector's discovered set.
*
* When active: null or next element in a discovered reference list
* maintained by the GC (or this if last)
* pending: next element in the pending-Reference list (null if last)
* inactive: null
*/ */
private transient Reference<T> discovered; /* used by VM */ private transient Reference<T> discovered;
/* High-priority thread to enqueue pending References /* High-priority thread to enqueue pending References
@ -141,17 +216,17 @@ public abstract class Reference<T> {
} }
/* /*
* Atomically get and clear (set to null) the VM's pending list. * Atomically get and clear (set to null) the VM's pending-Reference list.
*/ */
private static native Reference<Object> getAndClearReferencePendingList(); private static native Reference<Object> getAndClearReferencePendingList();
/* /*
* Test whether the VM's pending list contains any entries. * Test whether the VM's pending-Reference list contains any entries.
*/ */
private static native boolean hasReferencePendingList(); private static native boolean hasReferencePendingList();
/* /*
* Wait until the VM's pending list may be non-null. * Wait until the VM's pending-Reference list may be non-null.
*/ */
private static native void waitForReferencePendingList(); private static native void waitForReferencePendingList();

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -66,6 +66,7 @@ public class ReferenceQueue<T> {
return false; return false;
} }
assert queue == this; assert queue == this;
// Self-loop end, so if a FinalReference it remains inactive.
r.next = (head == null) ? r : head; r.next = (head == null) ? r : head;
head = r; head = r;
queueLength++; queueLength++;
@ -90,7 +91,10 @@ public class ReferenceQueue<T> {
// poll(). Volatiles ensure ordering. // poll(). Volatiles ensure ordering.
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Reference<? extends T> rn = r.next; Reference<? extends T> rn = r.next;
// Handle self-looped next as end of list designator.
head = (rn == r) ? null : rn; head = (rn == r) ? null : rn;
// Self-loop next rather than setting to null, so if a
// FinalReference it remains inactive.
r.next = r; r.next = r;
queueLength--; queueLength--;
if (r instanceof FinalReference) { if (r instanceof FinalReference) {