RFR: 8254315: Shenandoah: Concurrent weak reference processing [v10]

Zhengyu Gu zgu at openjdk.java.net
Wed Oct 21 17:07:18 UTC 2020


On Wed, 14 Oct 2020 14:32:28 GMT, Roman Kennke <rkennke at openjdk.org> wrote:

>> Until now, references (as in java.lang.ref.Reference and its subclasses WeakReference, SoftReference, PhantomReference and the non-public FinalReference - I'll collectively call them weak references for the purpose of clarity). Workloads that make heavvy use of such weak references will therefore potentially cause significant GC pauses.
>> 
>> There are 3 main items that contribute to pause time linear to number of references, or worse:
>> - We need to scan and consider each reference on the various 'discovered' lists.
>> - We need to mark through subgraph of objects that are reachable only through FinalReference. Notice that this is theoretically only bounded by the live data set size.
>> - Finally, all no-longer-reachable references need to be enqueued in the 'pending list'
>> 
>> The problem is somewhat mitigated by pre-cleaning the discovered list: Any weak reference that we find to be strongly reachable will be removed before we go into the final-mark-pause. However, that is only a band-aid.
>> 
>> The solution to this is two-fold:
>> 1. Extend concurrent marking to also mark the 'finalizable' subgraph of the heap. This requires to extend the marking bitmap to allow for two kinds of reachability: each object can now be strongly and finalizably reachable. Whenever marking encounters a FinalReference, it will mark through the referent and switch to 'finalizably' reachability for all objects starting from the referent. When marking encounters finalizably reachable objects while marking strongly, it will 'upgrade' reachability of such objects to strongly reachable. All of this can be done concurrently. Any encounter of a Reference (or subclass) object will enqueue that object into a thread-local 'discovered' list. Except for FinalReference, marking stops there, and does not mark through the referent.
>> 2. Concurrent processing is performed after the final-mark pause. GC workers scan all discovered lists that have been collected by concurrent marking, and depending on reachability of the referent, either drop the Reference, or enqueue it into the global 'pending' list (from where it will be processed by Java reference handler thread). In addition to that, we must ensure that no referents become resurrected by accessing Reference.get() on it. In order to achieve this, we employ special barriers in Reference.get() intrinsics that return NULL when the referent is not reachable.
>> 
>> Testing: hotspot_gc_shenadoah (release+fastdebug, x86+aarch64), specjvm+specjbb without regressions, tier1, tier2, vmTestbase_vm_metaspace, vmTestbase_nsk_jvmti, with -XX:+UseShenandoahGC without regressions, specjvm with various levels of verification
>
> Roman Kennke has updated the pull request incrementally with two additional commits since the last revision:
> 
>  - Add fallback support for new properties in ObjArrayChunkedTask
>  - Fix 32bit interpreter LRB-native call

src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.hpp line 64:

> 62:   void load_reference_barrier_not_null(MacroAssembler* masm, Register dst, Address load_addr);
> 63:   void load_reference_barrier_native(MacroAssembler* masm, Register dst, Address load_addr, bool native);
> 64: 

is_native parameter seems weird. Maybe invert to is_weak_ref? 
BTW, I think I am seeing compressed oops in conc-stack-scanning.

src/hotspot/cpu/x86/gc/shenandoah/shenandoahBarrierSetAssembler_x86.cpp line 399:

> 397: 
> 398:   save_xmm_registers(masm);
> 399:   if (UseCompressedOops && !native) {

What's problem you saw with compressed oop + native?

src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp line 42:

> 40:     WEAK
> 41:   };
> 42: private:

Declare as enum class for stronger typing?

src/hotspot/cpu/aarch64/gc/shenandoah/shenandoahBarrierSetAssembler_aarch64.cpp line 750:

> 748:   __ load_parameter(0, r0);
> 749:   __ load_parameter(1, r1);
> 750:   if (kind == ShenandoahBarrierSet::NATIVE) {

Use "switch" statement to be consistent with above

src/hotspot/share/gc/shenandoah/shenandoahReferenceProcessor.hpp line 139:

> 137: 
> 138:   // template <typename T>
> 139:   // void keep_alive(oop reference, ReferenceType type) const;

Remove this

src/hotspot/share/gc/shenandoah/shenandoahBarrierSet.hpp line 100:

> 98:   oop load_reference_barrier_not_null(oop obj);
> 99: 
> 100:   template <class T>

class -> typename also?

-------------

PR: https://git.openjdk.java.net/jdk/pull/505


More information about the shenandoah-dev mailing list