Summary
-------
Processing of j.l.ref.References in the G1 garbage collection pauses should be parallel by default and dynamically adapt the number of threads based on available work.
Problem
-------
Users currently need to manually turn on parallel processing for java.lang.ref.References during a G1 gc pause. This is an all-or-nothing setting: when turned on, G1 uses all available threads (as set by -XX:ParallelGCThreads) for this phase, that itself consists of several subphases, independent of available work. This means that often when in a particular garbage collection, or in a particular subphase of java.lang.ref.Reference processing there is not a lot of work to do, G1 still needs to start up and synchronize all threads for shutdown.
Starting up and synchronizing threads for shutdown immediately afterwards can take a relatively long amount of time due to interaction with the operating system.
So a user can select between potentially a slow java.lang.ref.Reference processing phase if there is lots of work to do, or performance losses due to java.lang.ref.Reference processing when there is not a lot of work to do.
Solution
--------
For G1 we will by default enable parallel java.lang.ref.Reference processing and use the existing mechanism where G1 tries to select a more optimal number of threads based on heuristics the amount of work per sub-phase of java.lang.ref.Reference processing.
Specification
-------------
For the G1 collector, the default value for the ParallelRefProcEnabled option will be true if more than one thread is used and not otherwise overridden. I.e.
--- old/src/hotspot/share/gc/g1/g1Arguments.cpp 2018-06-08 15:53:28.148957195 +0200
+++ new/src/hotspot/share/gc/g1/g1Arguments.cpp 2018-06-08 15:53:27.788945904 +0200
@@ -122,6 +122,10 @@
FLAG_SET_DEFAULT(GCPauseIntervalMillis, MaxGCPauseMillis + 1);
}
+ if (FLAG_IS_DEFAULT(ParallelRefProcEnabled) && ParallelGCThreads > 1) {
+ FLAG_SET_DEFAULT(ParallelRefProcEnabled, true);
+ }
+
log_trace(gc)("MarkStackSize: %uk MarkStackSizeMax: %uk", (unsigned int) (MarkStackSize / K), (uint) (MarkStackSizeMax / K));
The mechanism to select the number of parallel threads uses the existing flag ReferencesPerThread that determines the number of threads to be used for this phase: for every ReferencesPerThread java.lang.ref.References to work on, add another thread up to the current maximum as determined elsewhere.
To reiterate, only G1 observes ReferencesPerThread at this time. So using -XX:+ParallelRefProcEnabled with other collectors behaves the same as before, i.e. all currently available threads are used for parallel reference processing.