mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 06:45:07 +02:00
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:
parent
ace1b8a4c9
commit
6c20824cda
9 changed files with 197 additions and 210 deletions
|
@ -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",
|
|
||||||
// i.e. if next is non-NULL.
|
|
||||||
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);
|
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(discovered_addr);
|
||||||
}
|
|
||||||
cm->mark_and_push(next_addr);
|
|
||||||
klass->InstanceKlass::oop_pc_follow_contents(obj, cm);
|
klass->InstanceKlass::oop_pc_follow_contents(obj, cm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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,21 +450,11 @@ 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* next_addr = (T*)java_lang_ref_Reference::next_addr_raw(obj);
|
|
||||||
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);
|
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)) {
|
if (PSScavenge::should_scavenge(discovered_addr)) {
|
||||||
pm->claim_or_forward_depth(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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
// 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);
|
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));
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue