--- old/src/share/vm/classfile/javaClasses.cpp 2016-01-24 11:21:51.529806422 +0100 +++ new/src/share/vm/classfile/javaClasses.cpp 2016-01-24 11:21:51.450807782 +0100 @@ -3509,6 +3509,7 @@ int java_lang_ref_Reference::static_lock_offset; int java_lang_ref_Reference::static_pending_offset; int java_lang_ref_Reference::number_of_fake_oop_fields; +int java_lang_ref_Ephemeron::value_offset; int java_lang_ref_SoftReference::timestamp_offset; int java_lang_ref_SoftReference::static_clock_offset; int java_lang_ClassLoader::parent_offset; @@ -3684,7 +3685,10 @@ // The first field is for the discovered field added in 1.4 java_lang_ref_Reference::number_of_fake_oop_fields = 1; - // java_lang_ref_SoftReference Class + // java_lang_ref_Ephemeron Class + java_lang_ref_Ephemeron::value_offset = java_lang_ref_Ephemeron::hc_value_offset * x + header; + + // java_lang_ref_SoftReference Class java_lang_ref_SoftReference::timestamp_offset = align_size_up((java_lang_ref_SoftReference::hc_timestamp_offset * x + header), BytesPerLong); // Don't multiply static fields because they are always in wordSize units java_lang_ref_SoftReference::static_clock_offset = java_lang_ref_SoftReference::hc_static_clock_offset * x; --- old/src/share/vm/classfile/javaClasses.hpp 2016-01-24 11:21:51.866800621 +0100 +++ new/src/share/vm/classfile/javaClasses.hpp 2016-01-24 11:21:51.808801619 +0100 @@ -894,7 +894,7 @@ hc_referent_offset = 0, hc_queue_offset = 1, hc_next_offset = 2, - hc_discovered_offset = 3 // Is not last, see SoftRefs. + hc_discovered_offset = 3 // If not last, see SoftReference and Ephemeron. }; enum { hc_static_lock_offset = 0, @@ -955,6 +955,38 @@ }; +// Interface to java.lang.ref.WeakReference objects + +class java_lang_ref_WeakReference: public java_lang_ref_Reference { +}; + + +// Interface to java.lang.ref.Ephemeron objects + +class java_lang_ref_Ephemeron : public java_lang_ref_WeakReference { + public: + enum { + hc_value_offset = hc_discovered_offset + 1 + }; + + static int value_offset; + + // Accessors + static oop value(oop ref) { + return ref->obj_field(value_offset); + } + static void set_value(oop ref, oop value) { + ref->obj_field_put(value_offset, value); + } + static void set_value_raw(oop ref, oop value) { + ref->obj_field_put_raw(value_offset, value); + } + static HeapWord* value_addr(oop ref) { + return ref->obj_field_addr(value_offset); + } +}; + + // Interface to java.lang.ref.SoftReference objects class java_lang_ref_SoftReference: public java_lang_ref_Reference { --- old/src/share/vm/classfile/systemDictionary.cpp 2016-01-24 11:21:52.152795697 +0100 +++ new/src/share/vm/classfile/systemDictionary.cpp 2016-01-24 11:21:52.075797023 +0100 @@ -1974,12 +1974,13 @@ InstanceKlass::cast(WK_KLASS(Reference_klass))->set_reference_type(REF_OTHER); InstanceRefKlass::update_nonstatic_oop_maps(WK_KLASS(Reference_klass)); - initialize_wk_klasses_through(WK_KLASS_ENUM_NAME(Cleaner_klass), scan, CHECK); + initialize_wk_klasses_through(WK_KLASS_ENUM_NAME(PhantomReference_klass), scan, CHECK); InstanceKlass::cast(WK_KLASS(SoftReference_klass))->set_reference_type(REF_SOFT); InstanceKlass::cast(WK_KLASS(WeakReference_klass))->set_reference_type(REF_WEAK); + InstanceKlass::cast(WK_KLASS(Ephemeron_klass))->set_reference_type(REF_EPHEMERON); + InstanceRefKlass::update_nonstatic_ephemeron_oop_maps(WK_KLASS(Ephemeron_klass)); InstanceKlass::cast(WK_KLASS(FinalReference_klass))->set_reference_type(REF_FINAL); InstanceKlass::cast(WK_KLASS(PhantomReference_klass))->set_reference_type(REF_PHANTOM); - InstanceKlass::cast(WK_KLASS(Cleaner_klass))->set_reference_type(REF_CLEANER); // JSR 292 classes WKID jsr292_group_start = WK_KLASS_ENUM_NAME(MethodHandle_klass); --- old/src/share/vm/classfile/systemDictionary.hpp 2016-01-24 11:21:52.455790481 +0100 +++ new/src/share/vm/classfile/systemDictionary.hpp 2016-01-24 11:21:52.373791893 +0100 @@ -126,9 +126,9 @@ /* Preload ref klasses and set reference types */ \ do_klass(SoftReference_klass, java_lang_ref_SoftReference, Pre ) \ do_klass(WeakReference_klass, java_lang_ref_WeakReference, Pre ) \ + do_klass(Ephemeron_klass, java_lang_ref_Ephemeron, Pre ) \ do_klass(FinalReference_klass, java_lang_ref_FinalReference, Pre ) \ do_klass(PhantomReference_klass, java_lang_ref_PhantomReference, Pre ) \ - do_klass(Cleaner_klass, sun_misc_Cleaner, Pre ) \ do_klass(Finalizer_klass, java_lang_ref_Finalizer, Pre ) \ \ do_klass(Thread_klass, java_lang_Thread, Pre ) \ --- old/src/share/vm/classfile/vmSymbols.hpp 2016-01-24 11:21:52.740785575 +0100 +++ new/src/share/vm/classfile/vmSymbols.hpp 2016-01-24 11:21:52.678786642 +0100 @@ -80,9 +80,9 @@ template(java_lang_ref_Reference, "java/lang/ref/Reference") \ template(java_lang_ref_SoftReference, "java/lang/ref/SoftReference") \ template(java_lang_ref_WeakReference, "java/lang/ref/WeakReference") \ + template(java_lang_ref_Ephemeron, "java/lang/ref/Ephemeron") \ template(java_lang_ref_FinalReference, "java/lang/ref/FinalReference") \ template(java_lang_ref_PhantomReference, "java/lang/ref/PhantomReference") \ - template(sun_misc_Cleaner, "sun/misc/Cleaner") \ template(java_lang_ref_Finalizer, "java/lang/ref/Finalizer") \ template(java_lang_reflect_AccessibleObject, "java/lang/reflect/AccessibleObject") \ template(java_lang_reflect_Method, "java/lang/reflect/Method") \ --- old/src/share/vm/gc/parallel/psCompactionManager.cpp 2016-01-24 11:21:53.058780101 +0100 +++ new/src/share/vm/gc/parallel/psCompactionManager.cpp 2016-01-24 11:21:52.983781392 +0100 @@ -254,7 +254,13 @@ log_develop_trace(gc, ref)(" Process discovered as normal " PTR_FORMAT, p2i(discovered_addr)); cm->mark_and_push(discovered_addr); } + // Treat next as normal oop cm->mark_and_push(next_addr); + // Treat value as normal oop if Epehemeron + if (klass->reference_type() == REF_EPHEMERON) { + T* value_addr = (T*)java_lang_ref_Ephemeron::value_addr(obj); + cm->mark_and_push(value_addr); + } klass->InstanceKlass::oop_pc_follow_contents(obj, cm); } --- old/src/share/vm/gc/parallel/psParallelCompact.cpp 2016-01-24 11:21:53.356774971 +0100 +++ new/src/share/vm/gc/parallel/psParallelCompact.cpp 2016-01-24 11:21:53.299775952 +0100 @@ -3078,9 +3078,11 @@ #ifdef ASSERT template static void trace_reference_gc(const char *s, oop obj, + bool is_ephemeron, T* referent_addr, T* next_addr, - T* discovered_addr) { + T* discovered_addr, + T* value_addr) { log_develop_trace(gc, ref)("%s obj " PTR_FORMAT, s, p2i(obj)); log_develop_trace(gc, ref)(" referent_addr/* " PTR_FORMAT " / " PTR_FORMAT, p2i(referent_addr), referent_addr ? p2i(oopDesc::load_decode_heap_oop(referent_addr)) : NULL); @@ -3088,28 +3090,39 @@ p2i(next_addr), next_addr ? p2i(oopDesc::load_decode_heap_oop(next_addr)) : NULL); log_develop_trace(gc, ref)(" discovered_addr/* " PTR_FORMAT " / " PTR_FORMAT, p2i(discovered_addr), discovered_addr ? p2i(oopDesc::load_decode_heap_oop(discovered_addr)) : NULL); + if (is_ephemeron) { + log_develop_trace(gc, ref)(" value_addr/* " PTR_FORMAT " / " PTR_FORMAT, + p2i(value_addr), value_addr ? p2i(oopDesc::load_decode_heap_oop(value_addr)) : NULL); + } } #endif template -static void oop_pc_update_pointers_specialized(oop obj) { +static void oop_pc_update_pointers_specialized(oop obj, InstanceRefKlass* klass) { T* referent_addr = (T*)java_lang_ref_Reference::referent_addr(obj); PSParallelCompact::adjust_pointer(referent_addr); T* next_addr = (T*)java_lang_ref_Reference::next_addr(obj); PSParallelCompact::adjust_pointer(next_addr); T* discovered_addr = (T*)java_lang_ref_Reference::discovered_addr(obj); PSParallelCompact::adjust_pointer(discovered_addr); - debug_only(trace_reference_gc("InstanceRefKlass::oop_update_ptrs", obj, - referent_addr, next_addr, discovered_addr);) + if (klass->reference_type() == REF_EPHEMERON) { + T* value_addr = (T*)java_lang_ref_Ephemeron::value_addr(obj); + PSParallelCompact::adjust_pointer(value_addr); + debug_only(trace_reference_gc("InstanceRefKlass::oop_update_ptrs", obj, true, + referent_addr, next_addr, discovered_addr, value_addr);) + } else { + debug_only(trace_reference_gc("InstanceRefKlass::oop_update_ptrs", obj, false, + referent_addr, next_addr, discovered_addr, (T*)NULL);) + } } void InstanceRefKlass::oop_pc_update_pointers(oop obj) { InstanceKlass::oop_pc_update_pointers(obj); if (UseCompressedOops) { - oop_pc_update_pointers_specialized(obj); + oop_pc_update_pointers_specialized(obj, this); } else { - oop_pc_update_pointers_specialized(obj); + oop_pc_update_pointers_specialized(obj, this); } } --- old/src/share/vm/gc/parallel/psPromotionManager.cpp 2016-01-24 11:21:53.697769101 +0100 +++ new/src/share/vm/gc/parallel/psPromotionManager.cpp 2016-01-24 11:21:53.628770289 +0100 @@ -384,6 +384,13 @@ if (PSScavenge::should_scavenge(next_addr)) { pm->claim_or_forward_depth(next_addr); } + // Treat value as normal oop if Epehemeron + if (klass->reference_type() == REF_EPHEMERON) { + T* value_addr = (T*)java_lang_ref_Ephemeron::value_addr(obj); + if (PSScavenge::should_scavenge(value_addr)) { + pm->claim_or_forward_depth(value_addr); + } + } klass->InstanceKlass::oop_ps_push_contents(obj, pm); } --- old/src/share/vm/gc/serial/markSweep.cpp 2016-01-24 11:21:54.007763764 +0100 +++ new/src/share/vm/gc/serial/markSweep.cpp 2016-01-24 11:21:53.948764780 +0100 @@ -299,9 +299,11 @@ #ifdef ASSERT template static void trace_reference_gc(const char *s, oop obj, + bool is_ephemeron, T* referent_addr, T* next_addr, - T* discovered_addr) { + T* discovered_addr, + T* value_addr) { log_develop_trace(gc, ref)("%s obj " PTR_FORMAT, s, p2i(obj)); log_develop_trace(gc, ref)(" referent_addr/* " PTR_FORMAT " / " PTR_FORMAT, p2i(referent_addr), p2i(referent_addr ? (address)oopDesc::load_decode_heap_oop(referent_addr) : NULL)); @@ -309,18 +311,29 @@ p2i(next_addr), p2i(next_addr ? (address)oopDesc::load_decode_heap_oop(next_addr) : NULL)); log_develop_trace(gc, ref)(" discovered_addr/* " PTR_FORMAT " / " PTR_FORMAT, p2i(discovered_addr), p2i(discovered_addr ? (address)oopDesc::load_decode_heap_oop(discovered_addr) : NULL)); + if (is_ephemeron) { + log_develop_trace(gc, ref)(" value_addr/* " PTR_FORMAT " / " PTR_FORMAT, + p2i(value_addr), p2i(value_addr ? (address)oopDesc::load_decode_heap_oop(value_addr) : NULL)); + } } #endif -template void static adjust_object_specialized(oop obj) { +template void static adjust_object_specialized(oop obj, InstanceRefKlass* klass) { T* referent_addr = (T*)java_lang_ref_Reference::referent_addr(obj); MarkSweep::adjust_pointer(referent_addr); T* next_addr = (T*)java_lang_ref_Reference::next_addr(obj); MarkSweep::adjust_pointer(next_addr); T* discovered_addr = (T*)java_lang_ref_Reference::discovered_addr(obj); MarkSweep::adjust_pointer(discovered_addr); - debug_only(trace_reference_gc("InstanceRefKlass::oop_ms_adjust_pointers", obj, - referent_addr, next_addr, discovered_addr);) + if (klass->reference_type() == REF_EPHEMERON) { + T* value_addr = (T*) java_lang_ref_Ephemeron::value_addr(obj); + MarkSweep::adjust_pointer(value_addr); + debug_only(trace_reference_gc("InstanceRefKlass::oop_ms_adjust_pointers", obj, true, + referent_addr, next_addr, discovered_addr, value_addr);) + } else { + debug_only(trace_reference_gc("InstanceRefKlass::oop_ms_adjust_pointers", obj, false, + referent_addr, next_addr, discovered_addr, (T*)NULL);) + } } int InstanceRefKlass::oop_ms_adjust_pointers(oop obj) { @@ -328,9 +341,9 @@ InstanceKlass::oop_ms_adjust_pointers(obj); if (UseCompressedOops) { - adjust_object_specialized(obj); + adjust_object_specialized(obj, this); } else { - adjust_object_specialized(obj); + adjust_object_specialized(obj, this); } return size; } --- old/src/share/vm/gc/shared/referenceProcessor.cpp 2016-01-24 11:21:54.316758445 +0100 +++ new/src/share/vm/gc/shared/referenceProcessor.cpp 2016-01-24 11:21:54.251759564 +0100 @@ -117,9 +117,9 @@ } _discoveredSoftRefs = &_discovered_refs[0]; _discoveredWeakRefs = &_discoveredSoftRefs[_max_num_q]; - _discoveredFinalRefs = &_discoveredWeakRefs[_max_num_q]; + _discoveredEphemerons = &_discoveredWeakRefs[_max_num_q]; + _discoveredFinalRefs = &_discoveredEphemerons[_max_num_q]; _discoveredPhantomRefs = &_discoveredFinalRefs[_max_num_q]; - _discoveredCleanerRefs = &_discoveredPhantomRefs[_max_num_q]; // Initialize all entries to NULL for (uint i = 0; i < _max_num_q * number_of_subclasses_of_ref(); i++) { @@ -208,14 +208,22 @@ _soft_ref_timestamp_clock = java_lang_ref_SoftReference::clock(); - // Include cleaners in phantom statistics. We expect Cleaner - // references to be temporary, and don't want to deal with - // possible incompatibilities arising from making it more visible. ReferenceProcessorStats stats( total_count(_discoveredSoftRefs), total_count(_discoveredWeakRefs), + total_count(_discoveredEphemerons), total_count(_discoveredFinalRefs), - total_count(_discoveredPhantomRefs) + total_count(_discoveredCleanerRefs)); + total_count(_discoveredPhantomRefs)); + + // Ephemerons (phase2) before Soft references. This closes the hard-reachable + // set that arrises when the value of an ephemeron with a hard-reachable key + // refers to an otherwise weaker-than-hard-reachable key of some other ephemeron. + { + GCTraceTime(Debug, gc, ref) tt("Ephemeron-HardClosure", gc_timer); + // balance queues if needed 1st + balance_discovered_ephemerons(task_executor); + process_discovered_ephemerons_ph2(is_alive, keep_alive, complete_gc, task_executor); + } // Soft references { @@ -226,6 +234,15 @@ update_soft_ref_master_clock(); + // Ephemerons (phase2) again before Weak references. This closes the soft-reachable + // set that arrises when the value of an ephemeron with a soft-reachable key + // refers to an otherwise weaker-than-soft-reachable key of some other ephemeron. + { + GCTraceTime(Debug, gc, ref) tt("Ephemeron-SoftClosure", gc_timer); + process_discovered_ephemerons_ph2(is_alive, keep_alive, complete_gc, task_executor); + } + + // Weak references { GCTraceTime(Debug, gc, ref) tt("WeakReference", gc_timer); @@ -233,6 +250,12 @@ is_alive, keep_alive, complete_gc, task_executor); } + // Ephemerons (phase3). This clears any remaining ephemerons. + { + GCTraceTime(Debug, gc, ref) tt("Ephemeron-Clear", gc_timer); + process_discovered_ephemerons_ph3(is_alive, keep_alive, complete_gc, task_executor); + } + // Final references { GCTraceTime(Debug, gc, ref) tt("FinalReference", gc_timer); @@ -245,12 +268,6 @@ GCTraceTime(Debug, gc, ref) tt("PhantomReference", gc_timer); process_discovered_reflist(_discoveredPhantomRefs, NULL, true, is_alive, keep_alive, complete_gc, task_executor); - - // Process cleaners, but include them in phantom timing. We expect - // Cleaner references to be temporary, and don't want to deal with - // possible incompatibilities arising from making it more visible. - process_discovered_reflist(_discoveredCleanerRefs, NULL, true, - is_alive, keep_alive, complete_gc, task_executor); } // Weak global JNI references. It would make more sense (semantically) to @@ -266,8 +283,8 @@ process_phaseJNI(is_alive, keep_alive, complete_gc); } - log_debug(gc, ref)("Ref Counts: Soft: " SIZE_FORMAT " Weak: " SIZE_FORMAT " Final: " SIZE_FORMAT " Phantom: " SIZE_FORMAT, - stats.soft_count(), stats.weak_count(), stats.final_count(), stats.phantom_count()); + log_debug(gc, ref)("Ref Counts: Soft: " SIZE_FORMAT " Weak: " SIZE_FORMAT " Ephemeron: " SIZE_FORMAT " Final: " SIZE_FORMAT " Phantom: " SIZE_FORMAT, + stats.soft_count(), stats.weak_count(), stats.ephemeron_count(), stats.final_count(), stats.phantom_count()); log_develop_trace(gc, ref)("JNI Weak Reference count: " SIZE_FORMAT, count_jni_refs()); return stats; @@ -590,12 +607,153 @@ ) } -// Traverse the list and process the referents, by either -// clearing them or keeping them (and their reachable +bool +ReferenceProcessor::pp2_ephemerons_work(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); + // Temporary list used to reverse the order of ephemerons at each pass to avoid + // pathological cases where majority of revived ephemeron values point + // to ephemeron keys in the list "preceeding" this ephemeron. + DiscoveredList reversed_list; + bool ephemerons_removed = false; + while (iter.has_next()) { + iter.load_ptrs(DEBUG_ONLY(false /* allow_null_referent */)); + oop obj = iter.obj(); + DEBUG_ONLY(oop next = java_lang_ref_Reference::next(obj);) + assert(next == NULL, "Should not discover inactive Reference"); + if (iter.is_referent_alive()) { + log_develop_trace(gc, ref)("Dropping strongly reachable reference (" INTPTR_FORMAT ": %s)", + p2i(obj), obj->klass()->internal_name()); + // The referent (key) is reachable after all. + // Remove Ephemeron object from list. + iter.remove(); + // Update the referent (key) pointer as necessary: Note that this + // should not entail any recursive marking because the + // referent must already have been traversed. + iter.make_referent_alive(); + // Update the value pointer as necessary. + HeapWord* value_addr = java_lang_ref_Ephemeron::value_addr(obj); + if (UseCompressedOops) { + keep_alive->do_oop((narrowOop*) value_addr); + } else { + keep_alive->do_oop((oop*) value_addr); + } + ephemerons_removed = true; + // Close the newly reachable set as soon as the value is marked to be alive + // to increase the chance other ephemeron referents (keys) are revived as + // we proceed scanning the list. + complete_gc->do_void(); + } else { + // Referent (key) is not alive (yet) so move the ephemeron to a reversed_list + // to reverse scanning in the next pass. + iter.remove(); + HeapWord* discovered_addr = java_lang_ref_Reference::discovered_addr(obj); + oop current_head = reversed_list.head(); + // The last ref must have its discovered field pointing to itself. + oop next_discovered = (current_head != NULL) ? current_head : obj; + oop_store_raw(discovered_addr, next_discovered); + reversed_list.set_head(obj); + reversed_list.inc_length(1); + } + iter.move_to_next(); + } + assert(refs_list.length() == 0, "Should be empty"); + // replace the list with reversed list + refs_list = reversed_list; + NOT_PRODUCT( + if (iter.processed() > 0) { + log_develop_trace(gc, ref)(" Dropped " SIZE_FORMAT " active Ephemerons out of " SIZE_FORMAT + " Ephemerons in discovered list " INTPTR_FORMAT, + iter.processed() - refs_list.length(), iter.processed(), + p2i(refs_list.head())); + } + ) + return ephemerons_removed; +} + +bool +ReferenceProcessor::pp2_ephemerons_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); + // Temporary list used to reverse the order of ephemerons at each pass to avoid + // pathological cases where majority of revived ephemeron values point + // to ephemeron keys in the list "preceeding" this ephemeron. + DiscoveredList reversed_list; + bool ephemerons_removed = false; + while (iter.has_next()) { + iter.load_ptrs(DEBUG_ONLY(true /* allow_null_referent */)); + oop obj = iter.obj(); + HeapWord* next_addr = java_lang_ref_Reference::next_addr(obj); + oop next = java_lang_ref_Reference::next(obj); + if ((iter.referent() == NULL || iter.is_referent_alive() || + next != NULL)) { + assert(next->is_oop_or_null(), "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); + } + HeapWord* value_addr = java_lang_ref_Ephemeron::value_addr(obj); + if (UseCompressedOops) { + keep_alive->do_oop((narrowOop*) value_addr); + } else { + keep_alive->do_oop((oop*) value_addr); + } + ephemerons_removed = true; + // Close the newly reachable set as soon as the value is marked to be alive + // to increase the chance other ephemeron keys are revived as we proceed + // scanning the list + complete_gc->do_void(); + } else { + // Referent (key) is not alive (yet) so move the ephemeron to a reversed_list + // to reverse scanning in the next pass. + iter.remove(); + HeapWord* discovered_addr = java_lang_ref_Reference::discovered_addr(obj); + oop current_head = reversed_list.head(); + // The last ref must have its discovered field pointing to itself. + oop next_discovered = (current_head != NULL) ? current_head : obj; + oop_store_raw(discovered_addr, next_discovered); + reversed_list.set_head(obj); + reversed_list.inc_length(1); + } + iter.move_to_next(); + } + assert(refs_list.length() == 0, "Should be empty"); + // replace the list with reversed list + refs_list = reversed_list; + // Now close the newly reachable set at least once after the whole list has + // been scanned even if there were no ephemerons + if (!ephemerons_removed) { + complete_gc->do_void(); + } + NOT_PRODUCT( + if (iter.processed() > 0) { + log_develop_trace(gc, ref)(" Dropped " SIZE_FORMAT " active Ephemerons out of " SIZE_FORMAT + " Ephemerons in discovered list " INTPTR_FORMAT, + iter.processed() - refs_list.length(), iter.processed(), + p2i(refs_list.head())); + } + ) + return ephemerons_removed; +} + +// Traverse the list and process the referents (and values in case of Ephemerons), +// by either clearing them or keeping them (and their reachable // closure) alive. void ReferenceProcessor::process_phase3(DiscoveredList& refs_list, bool clear_referent, + bool has_ephemerons, BoolObjectClosure* is_alive, OopClosure* keep_alive, VoidClosure* complete_gc) { @@ -610,6 +768,12 @@ // keep the referent around iter.make_referent_alive(); } + if (has_ephemerons) { + assert(clear_referent, "Ephemerons should always be cleared"); + HeapWord* value_addr = java_lang_ref_Ephemeron::value_addr(iter.obj()); + // NULL out value pointer + oop_store_raw(value_addr, NULL); + } log_develop_trace(gc, ref)("Adding %sreference (" INTPTR_FORMAT ": %s) as pending", clear_referent ? "cleared " : "", p2i(iter.obj()), iter.obj()->klass()->internal_name()); assert(iter.obj()->is_oop(UseConcMarkSweepGC), "Adding a bad reference"); @@ -668,16 +832,25 @@ public: RefProcPhase2Task(ReferenceProcessor& ref_processor, DiscoveredList refs_lists[], + bool has_ephemerons, bool marks_oops_alive) - : ProcessTask(ref_processor, refs_lists, marks_oops_alive) + : ProcessTask(ref_processor, refs_lists, marks_oops_alive), + _has_ephemerons(has_ephemerons), ephemerons_removed(false) { } virtual void work(unsigned int i, BoolObjectClosure& is_alive, OopClosure& keep_alive, VoidClosure& complete_gc) { - _ref_processor.process_phase2(_refs_lists[i], - &is_alive, &keep_alive, &complete_gc); + bool r = _ref_processor.process_phase2(_refs_lists[i], _has_ephemerons, + &is_alive, &keep_alive, &complete_gc); + if (r) { + ephemerons_removed = true; + } } +private: + bool _has_ephemerons; +public: + bool ephemerons_removed; }; class RefProcPhase3Task: public AbstractRefProcTaskExecutor::ProcessTask { @@ -685,9 +858,11 @@ RefProcPhase3Task(ReferenceProcessor& ref_processor, DiscoveredList refs_lists[], bool clear_referent, + bool has_ephemerons, bool marks_oops_alive) : ProcessTask(ref_processor, refs_lists, marks_oops_alive), - _clear_referent(clear_referent) + _clear_referent(clear_referent), + _has_ephemerons(has_ephemerons) { } virtual void work(unsigned int i, BoolObjectClosure& is_alive, OopClosure& keep_alive, @@ -698,11 +873,12 @@ // Thread* thr = Thread::current(); // int refs_list_index = ((WorkerThread*)thr)->id(); // _ref_processor.process_phase3(_refs_lists[refs_list_index], _clear_referent, - _ref_processor.process_phase3(_refs_lists[i], _clear_referent, + _ref_processor.process_phase3(_refs_lists[i], _clear_referent, _has_ephemerons, &is_alive, &keep_alive, &complete_gc); } private: bool _clear_referent; + bool _has_ephemerons; }; #ifndef PRODUCT @@ -805,9 +981,9 @@ void ReferenceProcessor::balance_all_queues() { balance_queues(_discoveredSoftRefs); balance_queues(_discoveredWeakRefs); + balance_queues(_discoveredEphemerons); balance_queues(_discoveredFinalRefs); balance_queues(_discoveredPhantomRefs); - balance_queues(_discoveredCleanerRefs); } void ReferenceProcessor::process_discovered_reflist( @@ -856,22 +1032,117 @@ // Phase 2: // . Traverse the list and remove any refs whose referents are alive. if (mt_processing) { - RefProcPhase2Task phase2(*this, refs_lists, !discovery_is_atomic() /*marks_oops_alive*/); + RefProcPhase2Task phase2(*this, refs_lists, + false /*has_ephemerons*/, + !discovery_is_atomic() /*marks_oops_alive*/); task_executor->execute(phase2); } else { for (uint i = 0; i < _max_num_q; i++) { - process_phase2(refs_lists[i], is_alive, keep_alive, complete_gc); + process_phase2(refs_lists[i], + false /*has_ephemerons*/, + is_alive, keep_alive, complete_gc); } } // Phase 3: // . Traverse the list and process referents as appropriate. if (mt_processing) { - RefProcPhase3Task phase3(*this, refs_lists, clear_referent, true /*marks_oops_alive*/); + RefProcPhase3Task phase3(*this, refs_lists, clear_referent, + false /*has_ephemerons*/, + true /*marks_oops_alive*/); task_executor->execute(phase3); } else { for (uint i = 0; i < _max_num_q; i++) { process_phase3(refs_lists[i], clear_referent, + false /*has_ephemerons*/, + is_alive, keep_alive, complete_gc); + } + } +} + +// Balance ephemerons queues if needed +void ReferenceProcessor::balance_discovered_ephemerons( + AbstractRefProcTaskExecutor* task_executor) { + + bool mt_processing = task_executor != NULL && _processing_is_mt; + // If discovery used MT and a dynamic number of GC threads, then + // the queues must be balanced for correctness if fewer than the + // maximum number of queues were used. The number of queue used + // during discovery may be different than the number to be used + // for processing so don't depend of _num_q < _max_num_q as part + // of the test. + bool must_balance = _discovery_is_mt; + + if ((mt_processing && ParallelRefProcBalancingEnabled) || + must_balance) { + balance_queues(_discoveredEphemerons); + } +} + +// Process ephemerons, phase2 +void ReferenceProcessor::process_discovered_ephemerons_ph2( + BoolObjectClosure* is_alive, + OopClosure* keep_alive, + VoidClosure* complete_gc, + AbstractRefProcTaskExecutor* task_executor) { + + // Traverse the _discoveredEphemerons lists and for those ephemerons whose keys + // are alive, remove them from the list, mark their values alive and close the + // reachable set. Iterate until lists become stable while reversing the + // direction of scanning in each pass. + bool mt_processing = task_executor != NULL && _processing_is_mt; + bool ephemerons_removed; + bool forward_scan = true; + do { + if (mt_processing) { + RefProcPhase2Task phase2(*this, _discoveredEphemerons, + true /*has_ephemerons*/, + !discovery_is_atomic() /*marks_oops_alive*/); + task_executor->execute(phase2); + ephemerons_removed = phase2.ephemerons_removed; + } else { + ephemerons_removed = false; + // alternate direction of selecting individual lists for scanning to avoid + // pathological cases where majority of revived ephemeron values point + // to ephemeron keys in "previous" lists... + if (forward_scan) { + for (uint i = 0; i < _max_num_q; i++) { + ephemerons_removed |= process_phase2(_discoveredEphemerons[i], + true /*has_ephemerons*/, + is_alive, keep_alive, complete_gc); + } + } else { + for (uint i = _max_num_q - 1; i < (uint) - 1; i--) { + ephemerons_removed |= process_phase2(_discoveredEphemerons[i], + true /*has_ephemerons*/, + is_alive, keep_alive, complete_gc); + } + } + forward_scan = !forward_scan; + } + } while (ephemerons_removed); +} + +// Process ephemerons, phase3 +void ReferenceProcessor::process_discovered_ephemerons_ph3( + BoolObjectClosure* is_alive, + OopClosure* keep_alive, + VoidClosure* complete_gc, + AbstractRefProcTaskExecutor* task_executor) { + + // Traverse the _discoveredEphemerons lists and clear ephemerons. + bool mt_processing = task_executor != NULL && _processing_is_mt; + if (mt_processing) { + RefProcPhase3Task phase3(*this, _discoveredEphemerons, + true /*clear_referent*/, + true /*has_ephemerons*/, + true /*marks_oops_alive*/); + task_executor->execute(phase3); + } else { + for (uint i = 0; i < _max_num_q; i++) { + process_phase3(_discoveredEphemerons[i], + true /*clear_referent*/, + true /*has_ephemerons*/, is_alive, keep_alive, complete_gc); } } @@ -906,15 +1177,15 @@ case REF_WEAK: list = &_discoveredWeakRefs[id]; break; + case REF_EPHEMERON: + list = &_discoveredEphemerons[id]; + break; case REF_FINAL: list = &_discoveredFinalRefs[id]; break; case REF_PHANTOM: list = &_discoveredPhantomRefs[id]; break; - case REF_CLEANER: - list = &_discoveredCleanerRefs[id]; - break; case REF_NONE: // we should not reach here if we are an InstanceRefKlass default: @@ -1116,6 +1387,39 @@ YieldClosure* yield, GCTimer* gc_timer) { + // Ephemerons - iterate until the lists become stable + { + GCTraceTime(Debug, gc, ref) tt("Preclean Ephemerons", gc_timer); + bool ephemerons_removed; + bool forward_scan = true; + do { + ephemerons_removed = false; + // alternate direction of selecting individual lists for scanning to avoid + // pathological cases where majority of revived ephemeron values point + // to ephemeron keys in "previous" lists... + if (forward_scan) { + for (uint i = 0; i < _max_num_q; i++) { + if (yield->should_return()) { + return; + } + ephemerons_removed |= + preclean_discovered_ephemerons_reflist(_discoveredEphemerons[i], is_alive, + keep_alive, complete_gc, yield); + } + } else { + for (uint i = _max_num_q - 1; i < (uint) - 1; i--) { + if (yield->should_return()) { + return; + } + ephemerons_removed |= + preclean_discovered_ephemerons_reflist(_discoveredEphemerons[i], is_alive, + keep_alive, complete_gc, yield); + } + } + forward_scan = !forward_scan; + } while (ephemerons_removed); + } + // Soft references { GCTraceTime(Debug, gc, ref) tm("Preclean SoftReferences", gc_timer); @@ -1162,17 +1466,6 @@ preclean_discovered_reflist(_discoveredPhantomRefs[i], is_alive, keep_alive, complete_gc, yield); } - - // Cleaner references. Included in timing for phantom references. We - // expect Cleaner references to be temporary, and don't want to deal with - // possible incompatibilities arising from making it more visible. - for (uint i = 0; i < _max_num_q; i++) { - if (yield->should_return()) { - return; - } - preclean_discovered_reflist(_discoveredCleanerRefs[i], is_alive, - keep_alive, complete_gc, yield); - } } } @@ -1227,6 +1520,83 @@ } ) } +// The same as above, but specialized for ephemerons and returns true if any +// ephemerons were removed from ref list. +bool +ReferenceProcessor::preclean_discovered_ephemerons_reflist(DiscoveredList& refs_list, + BoolObjectClosure* is_alive, + OopClosure* keep_alive, + VoidClosure* complete_gc, + YieldClosure* yield) { + DiscoveredListIterator iter(refs_list, keep_alive, is_alive); + // Temporary list used to reverse the order of ephemerons at each pass to avoid + // pathological cases where majority of revived ephemeron values point + // to ephemeron keys in the list "preceeding" this ephemeron. + DiscoveredList reversed_list; + bool ephemerons_removed = false; + while (iter.has_next()) { + iter.load_ptrs(DEBUG_ONLY(true /* allow_null_referent */)); + oop obj = iter.obj(); + oop next = java_lang_ref_Reference::next(obj); + if (iter.referent() == NULL || iter.is_referent_alive() || + next != NULL) { + // 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 Ephemeron (" INTPTR_FORMAT ": %s)", + p2i(obj), obj->klass()->internal_name()); + // Remove Reference object from list + iter.remove(); + // Keep alive its cohort. + iter.make_referent_alive(); + if (UseCompressedOops) { + narrowOop* next_addr = (narrowOop*) java_lang_ref_Reference::next_addr(obj); + keep_alive->do_oop(next_addr); + } else { + oop* next_addr = (oop*) java_lang_ref_Reference::next_addr(obj); + keep_alive->do_oop(next_addr); + } + HeapWord* value_addr = java_lang_ref_Ephemeron::value_addr(obj); + if (UseCompressedOops) { + keep_alive->do_oop((narrowOop*) value_addr); + } else { + keep_alive->do_oop((oop*) value_addr); + } + ephemerons_removed = true; + // Close the newly reachable set as soon as the value is marked to be alive + // to increase the chance other ephemeron referents (keys) are revived as + // we proceed scanning the list. + complete_gc->do_void(); + } else { + // Referent (key) is not alive (yet) so move the ephemeron to a reversed_list + // to reverse scanning in the next pass. + iter.remove(); + HeapWord* discovered_addr = java_lang_ref_Reference::discovered_addr(obj); + oop current_head = reversed_list.head(); + // The last ref must have its discovered field pointing to itself. + oop next_discovered = (current_head != NULL) ? current_head : obj; + oop_store_raw(discovered_addr, next_discovered); + reversed_list.set_head(obj); + reversed_list.inc_length(1); + } + iter.move_to_next(); + } + assert(refs_list.length() == 0, "Should be empty"); + // replace the list with reversed list + refs_list = reversed_list; + // Close the reachable set even if no ephemeron was removed from list + if (!ephemerons_removed) { + complete_gc->do_void(); + } + NOT_PRODUCT( + if (iter.processed() > 0) { + log_develop_trace(gc, ref)(" Dropped " SIZE_FORMAT " Ephemerons out of " SIZE_FORMAT + " Ephemerons in discovered list " INTPTR_FORMAT, + iter.processed() - refs_list.length(), iter.processed(), + p2i(refs_list.head())); + } + ) + return ephemerons_removed; +} const char* ReferenceProcessor::list_name(uint i) { assert(i <= _max_num_q * number_of_subclasses_of_ref(), @@ -1236,9 +1606,9 @@ switch (j) { case 0: return "SoftRef"; case 1: return "WeakRef"; - case 2: return "FinalRef"; - case 3: return "PhantomRef"; - case 4: return "CleanerRef"; + case 2: return "Ephemeron"; + case 3: return "FinalRef"; + case 4: return "PhantomRef"; } ShouldNotReachHere(); return NULL; --- old/src/share/vm/gc/shared/referenceProcessor.hpp 2016-01-24 11:21:54.600753556 +0100 +++ new/src/share/vm/gc/shared/referenceProcessor.hpp 2016-01-24 11:21:54.524754864 +0100 @@ -242,12 +242,12 @@ // Arrays of lists of oops, one per thread (pointers into master array above) DiscoveredList* _discoveredSoftRefs; DiscoveredList* _discoveredWeakRefs; + DiscoveredList* _discoveredEphemerons; DiscoveredList* _discoveredFinalRefs; DiscoveredList* _discoveredPhantomRefs; - DiscoveredList* _discoveredCleanerRefs; - + public: - static int number_of_subclasses_of_ref() { return (REF_CLEANER - REF_OTHER); } + static int number_of_subclasses_of_ref() { return (REF_PHANTOM - REF_OTHER); } uint num_q() { return _num_q; } uint max_num_q() { return _max_num_q; } @@ -271,6 +271,21 @@ VoidClosure* complete_gc, AbstractRefProcTaskExecutor* task_executor); + // Balance ephemerons queues if needed + void balance_discovered_ephemerons(AbstractRefProcTaskExecutor* task_executor); + + // Process ephemerons, phase2 + void process_discovered_ephemerons_ph2(BoolObjectClosure* is_alive, + OopClosure* keep_alive, + VoidClosure* complete_gc, + AbstractRefProcTaskExecutor* task_executor); + + // Process ephemerons, phase3 + void process_discovered_ephemerons_ph3(BoolObjectClosure* is_alive, + OopClosure* keep_alive, + VoidClosure* complete_gc, + AbstractRefProcTaskExecutor* task_executor); + void process_phaseJNI(BoolObjectClosure* is_alive, OopClosure* keep_alive, VoidClosure* complete_gc); @@ -284,18 +299,30 @@ OopClosure* keep_alive, VoidClosure* complete_gc); // Phase2: remove all those references whose referents are - // reachable. - inline void process_phase2(DiscoveredList& refs_list, + // reachable. Return true if any ephemerons were removed. + inline bool process_phase2(DiscoveredList& refs_list, + bool has_ephemerons, BoolObjectClosure* is_alive, OopClosure* keep_alive, VoidClosure* complete_gc) { - if (discovery_is_atomic()) { - // complete_gc is ignored in this case for this phase - pp2_work(refs_list, is_alive, keep_alive); - } else { + if (has_ephemerons) { assert(complete_gc != NULL, "Error"); - pp2_work_concurrent_discovery(refs_list, is_alive, - keep_alive, complete_gc); + if (discovery_is_atomic()) { + return pp2_ephemerons_work(refs_list, is_alive, keep_alive, complete_gc); + } else { + return pp2_ephemerons_work_concurrent_discovery(refs_list, is_alive, + keep_alive, complete_gc); + } + } else { + if (discovery_is_atomic()) { + // 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); + } + return false; } } // Work methods in support of process_phase2 @@ -307,10 +334,21 @@ BoolObjectClosure* is_alive, OopClosure* keep_alive, VoidClosure* complete_gc); + bool pp2_ephemerons_work( + DiscoveredList& refs_list, + BoolObjectClosure* is_alive, + OopClosure* keep_alive, + VoidClosure* complete_gc); + bool pp2_ephemerons_work_concurrent_discovery( + DiscoveredList& refs_list, + BoolObjectClosure* is_alive, + OopClosure* keep_alive, + VoidClosure* complete_gc); // Phase3: process the referents by either clearing them // or keeping them alive (and their closure) void process_phase3(DiscoveredList& refs_list, bool clear_referent, + bool has_ephemerons, BoolObjectClosure* is_alive, OopClosure* keep_alive, VoidClosure* complete_gc); @@ -348,7 +386,15 @@ OopClosure* keep_alive, VoidClosure* complete_gc, YieldClosure* yield); - + // The same as above, but specialized for ephemerons and returns true + // if any ephemerons were removed from the list. + bool preclean_discovered_ephemerons_reflist( + DiscoveredList& refs_list, + BoolObjectClosure* is_alive, + OopClosure* keep_alive, + VoidClosure* complete_gc, + YieldClosure* yield); + // round-robin mod _num_q (not: _not_ mode _max_num_q) uint next_id() { uint id = _next_id; --- old/src/share/vm/gc/shared/referenceProcessorStats.hpp 2016-01-24 11:21:54.894748495 +0100 +++ new/src/share/vm/gc/shared/referenceProcessorStats.hpp 2016-01-24 11:21:54.813749889 +0100 @@ -34,6 +34,7 @@ class ReferenceProcessorStats { size_t _soft_count; size_t _weak_count; + size_t _ephemeron_count; size_t _final_count; size_t _phantom_count; @@ -41,15 +42,18 @@ ReferenceProcessorStats() : _soft_count(0), _weak_count(0), + _ephemeron_count(0), _final_count(0), _phantom_count(0) {} ReferenceProcessorStats(size_t soft_count, size_t weak_count, + size_t ephemeron_count, size_t final_count, size_t phantom_count) : _soft_count(soft_count), _weak_count(weak_count), + _ephemeron_count(ephemeron_count), _final_count(final_count), _phantom_count(phantom_count) {} @@ -62,6 +66,10 @@ return _weak_count; } + size_t ephemeron_count() const { + return _ephemeron_count; + } + size_t final_count() const { return _final_count; } --- old/src/share/vm/memory/referenceType.hpp 2016-01-24 11:21:55.177743623 +0100 +++ new/src/share/vm/memory/referenceType.hpp 2016-01-24 11:21:55.096745018 +0100 @@ -33,10 +33,10 @@ REF_NONE, // Regular class REF_OTHER, // Subclass of java/lang/ref/Reference, but not subclass of one of the classes below REF_SOFT, // Subclass of java/lang/ref/SoftReference - REF_WEAK, // Subclass of java/lang/ref/WeakReference + REF_WEAK, // Subclass of java/lang/ref/WeakReference (but not of Ephemeron) + REF_EPHEMERON, // Subclass of java/lang/ref/Ephemeron REF_FINAL, // Subclass of java/lang/ref/FinalReference - REF_PHANTOM, // Subclass of java/lang/ref/PhantomReference - REF_CLEANER // Subclass of sun/misc/Cleaner + REF_PHANTOM // Subclass of java/lang/ref/PhantomReference }; #endif // SHARE_VM_MEMORY_REFERENCETYPE_HPP --- old/src/share/vm/oops/instanceRefKlass.cpp 2016-01-24 11:21:55.448738958 +0100 +++ new/src/share/vm/oops/instanceRefKlass.cpp 2016-01-24 11:21:55.369740318 +0100 @@ -44,7 +44,7 @@ // Check that we have the right class debug_only(static bool first_time = true); assert(k == SystemDictionary::Reference_klass() && first_time, - "Invalid update of maps"); + "Invalid update of Reference maps"); debug_only(first_time = false); assert(ik->nonstatic_oop_map_count() == 1, "just checking"); @@ -69,6 +69,35 @@ } } +void InstanceRefKlass::update_nonstatic_ephemeron_oop_maps(Klass* k) { + // Clear the nonstatic oop-map entry corresponding to value field. + // It is treated specially by the garbage collector. + InstanceKlass* ik = InstanceKlass::cast(k); + + // Check that we have the right class + debug_only(static bool first_time = true); + assert(k == SystemDictionary::Ephemeron_klass() && first_time, + "Invalid update of Ephemeron maps"); + debug_only(first_time = false); + assert(ik->nonstatic_oop_map_count() == 2, "just checking"); + + // skip 1st map as it's the copy of the augmented Reference's map + OopMapBlock* map = ik->start_of_nonstatic_oop_maps() + 1; + + debug_only(int offset = java_lang_ref_Ephemeron::value_offset); + + if (UseSharedSpaces) { + assert(map->offset() == offset && map->count() == 0, + "just checking"); + } else { + assert(map->offset() == offset && map->count() == 1, + "just checking"); + + // Update map to have 0 oops. + map->set_count(0); + } +} + // Verification --- old/src/share/vm/oops/instanceRefKlass.hpp 2016-01-24 11:21:55.716734345 +0100 +++ new/src/share/vm/oops/instanceRefKlass.hpp 2016-01-24 11:21:55.640735653 +0100 @@ -126,6 +126,9 @@ // 'discovered' will look like non-oops static void update_nonstatic_oop_maps(Klass* k); + // Update non-static ephemeron oop maps so 'value' will look like non-oop + static void update_nonstatic_ephemeron_oop_maps(Klass* k); + public: // Verification void oop_verify_on(oop obj, outputStream* st); --- old/src/share/vm/oops/instanceRefKlass.inline.hpp 2016-01-24 11:21:56.010729283 +0100 +++ new/src/share/vm/oops/instanceRefKlass.inline.hpp 2016-01-24 11:21:55.934730592 +0100 @@ -67,6 +67,13 @@ if (contains(next_addr)) { Devirtualizer::do_oop(closure, next_addr); } + // treat value as normal oop if Ephemeron + if (reference_type() == REF_EPHEMERON) { + T* value_addr = (T*)java_lang_ref_Ephemeron::value_addr(obj); + if (contains(value_addr)) { + Devirtualizer::do_oop(closure, value_addr); + } + } } class AlwaysContains { --- old/src/share/vm/runtime/vmStructs.cpp 2016-01-24 11:21:56.352723396 +0100 +++ new/src/share/vm/runtime/vmStructs.cpp 2016-01-24 11:21:56.274724739 +0100 @@ -704,9 +704,9 @@ static_field(SystemDictionary, WK_KLASS(Reference_klass), InstanceKlass*) \ static_field(SystemDictionary, WK_KLASS(SoftReference_klass), InstanceKlass*) \ static_field(SystemDictionary, WK_KLASS(WeakReference_klass), InstanceKlass*) \ + static_field(SystemDictionary, WK_KLASS(Ephemeron_klass), InstanceKlass*) \ static_field(SystemDictionary, WK_KLASS(FinalReference_klass), InstanceKlass*) \ static_field(SystemDictionary, WK_KLASS(PhantomReference_klass), InstanceKlass*) \ - static_field(SystemDictionary, WK_KLASS(Cleaner_klass), InstanceKlass*) \ static_field(SystemDictionary, WK_KLASS(Finalizer_klass), InstanceKlass*) \ static_field(SystemDictionary, WK_KLASS(Thread_klass), InstanceKlass*) \ static_field(SystemDictionary, WK_KLASS(ThreadGroup_klass), InstanceKlass*) \