RFR: 8064721: The card tables only ever need two covering regions

Erik Helin erik.helin at oracle.com
Wed Nov 12 18:57:37 UTC 2014

Hi all,

this patch removes the max_covering_regions argument from the BarrierSet 
constructor, since all the code using a BarrierSet only needs two 
covering regions. Having the maximum number of covering regions as a 
constant is desirable because it makes it much easier to reason about 
the code in all the various card tables (CardTableRS, CardTableModRefBS, 
G1SATBCardTableModRefBS, CardTableExtension).

The only way a covering region can be added is via a call to 
find_covering_region_by_base adds a region if there is no covering 
region already having its argument `base` as start address. The only 
caller to find_covering_region_by_base is 

To show that two covering regions are enough, one has to show that 
callers of CardTableModRefBS::resize_covered_region in different 
generations always pass an MemRegion argument with a start address that 
is always the same. This way, we only need one covering region per 
generation, and we only ever have two generations.

There calls to CardTableModRefBS::resize_covering_region comes from 
(found via grep, filtered declarations, definitions and forwarding):
- psYoungGen.cpp
- psOldGen.cpp
- asPSYoungGen.cpp
- concurrentMarkSweepGeneration.cpp
- generation.cpp
- defNewGeneration.cpp

The calls in defNewGeneration.cpp always uses _virtual_space.low() as 
the start address of the MemRegion argument, and _virtual_space.low() 
never changes.

In generation.cpp, the calls from the class OneContigSpaceCardGeneration 
always use _the_space->bottom() as start and that won't change. The call 
from CardGeneration uses `start`, which is the same as rs.base(), which 
is the same as _virtual_space.low(), which (by looking in 
tenuredGeneration.cpp) is the same _the_space->bottom(). So both 
CardGeneration and OneContigSpaceCardGeneration use the same start address.

The calls in concurrentMarkSweepGeneration.cpp uses _cmsSpace->bottom() 
as the start of the MemRegion and that won't change.

The calls in asPSYoungGen.cpp, psOldGen.cpp and psYoungGen.cpp all uses 
virtual_space()->low() as the start address for the MemRegion. This 
address might change, but the calls from these files will reach 
CardTableExtension::resize_covered_region, which explicitly handles the 
case when the start address has changed and ensures now new covering 
region is added (see comments and code in cardTableExtension.cpp).

The only GC that specified 2 as max_covering_regions was G1. ParallelGC 
specified 3, most likely because each generation needed one and PermGen 
needed one and this was not updated when PermGen was removed. The 
generation collectors specified 4, because each generation wanted one 
each and PermGen wanted two (one one for the main space and one for the 
shared spaces).

The other variable that was made static const is 
CardTableRS::_regions_to_iterate. This was previously set to 
max_covered_regions - 1 by the framework collectors, so 4 - 1 = 3. I 
also added a comment explaining why it needs to be 3 and not 2.



- Running all JTReg tests locally
- Ad-hoc (Window 32/64, Linux 32/64, OS X 64, Solaris x86-64/SPARC):
   - Dacapo, Kitchensink, Weblogic+medrec, runThese
   - nsk.regression, vm.gc, vm.oom, vm.parallel_class_loading,
     vm.regression, vm.runtime, vm.compiler, vm.runtime, vm.signal,
     vm.debug, nsk.sysdict + some others
   - nashorn regression tests
   - jruby quick + slow
   - JTReg:
     - hotspot
     - jdk_core, jdk_svc


More information about the hotspot-gc-dev mailing list