8200545: Improve filter for enqueued deferred cards
authortschatzl
Tue, 14 May 2019 15:36:26 +0200
changeset 54843 25c329958c70
parent 54842 f9c8e16db3dd
child 54844 571732021d59
8200545: Improve filter for enqueued deferred cards Reviewed-by: kbarrett, sangheki
src/hotspot/share/gc/g1/g1AllocRegion.hpp
src/hotspot/share/gc/g1/g1Allocator.cpp
src/hotspot/share/gc/g1/g1Allocator.hpp
src/hotspot/share/gc/g1/g1Allocator.inline.hpp
src/hotspot/share/gc/g1/g1BiasedArray.hpp
src/hotspot/share/gc/g1/g1CollectedHeap.cpp
src/hotspot/share/gc/g1/g1CollectedHeap.hpp
src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp
src/hotspot/share/gc/g1/g1CollectionSet.cpp
src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp
src/hotspot/share/gc/g1/g1GCPhaseTimes.hpp
src/hotspot/share/gc/g1/g1HeapRegionAttr.hpp
src/hotspot/share/gc/g1/g1HeapVerifier.cpp
src/hotspot/share/gc/g1/g1InCSetState.hpp
src/hotspot/share/gc/g1/g1OopClosures.hpp
src/hotspot/share/gc/g1/g1OopClosures.inline.hpp
src/hotspot/share/gc/g1/g1ParScanThreadState.cpp
src/hotspot/share/gc/g1/g1ParScanThreadState.hpp
src/hotspot/share/gc/g1/g1ParScanThreadState.inline.hpp
src/hotspot/share/gc/g1/g1Policy.hpp
src/hotspot/share/gc/g1/g1RemSet.cpp
src/hotspot/share/gc/g1/g1RemSet.hpp
test/hotspot/jtreg/gc/g1/TestEagerReclaimHumongousRegionsLog.java
test/hotspot/jtreg/gc/g1/TestGCLogMessages.java
--- a/src/hotspot/share/gc/g1/g1AllocRegion.hpp	Tue May 14 08:52:51 2019 -0400
+++ b/src/hotspot/share/gc/g1/g1AllocRegion.hpp	Tue May 14 15:36:26 2019 +0200
@@ -27,7 +27,7 @@
 
 #include "gc/g1/heapRegion.hpp"
 #include "gc/g1/g1EvacStats.hpp"
-#include "gc/g1/g1InCSetState.hpp"
+#include "gc/g1/g1HeapRegionAttr.hpp"
 
 class G1CollectedHeap;
 
@@ -249,14 +249,14 @@
 class G1GCAllocRegion : public G1AllocRegion {
 protected:
   G1EvacStats* _stats;
-  InCSetState::in_cset_state_t _purpose;
+  G1HeapRegionAttr::region_type_t _purpose;
 
   virtual HeapRegion* allocate_new_region(size_t word_size, bool force);
   virtual void retire_region(HeapRegion* alloc_region, size_t allocated_bytes);
 
   virtual size_t retire(bool fill_up);
 
-  G1GCAllocRegion(const char* name, bool bot_updates, G1EvacStats* stats, InCSetState::in_cset_state_t purpose)
+  G1GCAllocRegion(const char* name, bool bot_updates, G1EvacStats* stats, G1HeapRegionAttr::region_type_t purpose)
   : G1AllocRegion(name, bot_updates), _stats(stats), _purpose(purpose) {
     assert(stats != NULL, "Must pass non-NULL PLAB statistics");
   }
@@ -265,13 +265,13 @@
 class SurvivorGCAllocRegion : public G1GCAllocRegion {
 public:
   SurvivorGCAllocRegion(G1EvacStats* stats)
-  : G1GCAllocRegion("Survivor GC Alloc Region", false /* bot_updates */, stats, InCSetState::Young) { }
+  : G1GCAllocRegion("Survivor GC Alloc Region", false /* bot_updates */, stats, G1HeapRegionAttr::Young) { }
 };
 
 class OldGCAllocRegion : public G1GCAllocRegion {
 public:
   OldGCAllocRegion(G1EvacStats* stats)
-  : G1GCAllocRegion("Old GC Alloc Region", true /* bot_updates */, stats, InCSetState::Old) { }
+  : G1GCAllocRegion("Old GC Alloc Region", true /* bot_updates */, stats, G1HeapRegionAttr::Old) { }
 
   // This specialization of release() makes sure that the last card that has
   // been allocated into has been completely filled by a dummy object.  This
--- a/src/hotspot/share/gc/g1/g1Allocator.cpp	Tue May 14 08:52:51 2019 -0400
+++ b/src/hotspot/share/gc/g1/g1Allocator.cpp	Tue May 14 15:36:26 2019 +0200
@@ -39,8 +39,8 @@
   _survivor_is_full(false),
   _old_is_full(false),
   _mutator_alloc_region(),
-  _survivor_gc_alloc_region(heap->alloc_buffer_stats(InCSetState::Young)),
-  _old_gc_alloc_region(heap->alloc_buffer_stats(InCSetState::Old)),
+  _survivor_gc_alloc_region(heap->alloc_buffer_stats(G1HeapRegionAttr::Young)),
+  _old_gc_alloc_region(heap->alloc_buffer_stats(G1HeapRegionAttr::Old)),
   _retained_old_gc_alloc_region(NULL) {
 }
 
@@ -161,7 +161,7 @@
 }
 
 
-HeapWord* G1Allocator::par_allocate_during_gc(InCSetState dest,
+HeapWord* G1Allocator::par_allocate_during_gc(G1HeapRegionAttr dest,
                                               size_t word_size) {
   size_t temp = 0;
   HeapWord* result = par_allocate_during_gc(dest, word_size, word_size, &temp);
@@ -171,14 +171,14 @@
   return result;
 }
 
-HeapWord* G1Allocator::par_allocate_during_gc(InCSetState dest,
+HeapWord* G1Allocator::par_allocate_during_gc(G1HeapRegionAttr dest,
                                               size_t min_word_size,
                                               size_t desired_word_size,
                                               size_t* actual_word_size) {
-  switch (dest.value()) {
-    case InCSetState::Young:
+  switch (dest.type()) {
+    case G1HeapRegionAttr::Young:
       return survivor_attempt_allocation(min_word_size, desired_word_size, actual_word_size);
-    case InCSetState::Old:
+    case G1HeapRegionAttr::Old:
       return old_attempt_allocation(min_word_size, desired_word_size, actual_word_size);
     default:
       ShouldNotReachHere();
@@ -246,22 +246,22 @@
 G1PLABAllocator::G1PLABAllocator(G1Allocator* allocator) :
   _g1h(G1CollectedHeap::heap()),
   _allocator(allocator),
-  _surviving_alloc_buffer(_g1h->desired_plab_sz(InCSetState::Young)),
-  _tenured_alloc_buffer(_g1h->desired_plab_sz(InCSetState::Old)),
+  _surviving_alloc_buffer(_g1h->desired_plab_sz(G1HeapRegionAttr::Young)),
+  _tenured_alloc_buffer(_g1h->desired_plab_sz(G1HeapRegionAttr::Old)),
   _survivor_alignment_bytes(calc_survivor_alignment_bytes()) {
-  for (uint state = 0; state < InCSetState::Num; state++) {
+  for (uint state = 0; state < G1HeapRegionAttr::Num; state++) {
     _direct_allocated[state] = 0;
     _alloc_buffers[state] = NULL;
   }
-  _alloc_buffers[InCSetState::Young] = &_surviving_alloc_buffer;
-  _alloc_buffers[InCSetState::Old]  = &_tenured_alloc_buffer;
+  _alloc_buffers[G1HeapRegionAttr::Young] = &_surviving_alloc_buffer;
+  _alloc_buffers[G1HeapRegionAttr::Old]  = &_tenured_alloc_buffer;
 }
 
 bool G1PLABAllocator::may_throw_away_buffer(size_t const allocation_word_sz, size_t const buffer_size) const {
   return (allocation_word_sz * 100 < buffer_size * ParallelGCBufferWastePct);
 }
 
-HeapWord* G1PLABAllocator::allocate_direct_or_new_plab(InCSetState dest,
+HeapWord* G1PLABAllocator::allocate_direct_or_new_plab(G1HeapRegionAttr dest,
                                                        size_t word_sz,
                                                        bool* plab_refill_failed) {
   size_t plab_word_size = _g1h->desired_plab_sz(dest);
@@ -300,17 +300,17 @@
   // Try direct allocation.
   HeapWord* result = _allocator->par_allocate_during_gc(dest, word_sz);
   if (result != NULL) {
-    _direct_allocated[dest.value()] += word_sz;
+    _direct_allocated[dest.type()] += word_sz;
   }
   return result;
 }
 
-void G1PLABAllocator::undo_allocation(InCSetState dest, HeapWord* obj, size_t word_sz) {
+void G1PLABAllocator::undo_allocation(G1HeapRegionAttr dest, HeapWord* obj, size_t word_sz) {
   alloc_buffer(dest)->undo_allocation(obj, word_sz);
 }
 
 void G1PLABAllocator::flush_and_retire_stats() {
-  for (uint state = 0; state < InCSetState::Num; state++) {
+  for (uint state = 0; state < G1HeapRegionAttr::Num; state++) {
     PLAB* const buf = _alloc_buffers[state];
     if (buf != NULL) {
       G1EvacStats* stats = _g1h->alloc_buffer_stats(state);
@@ -323,7 +323,7 @@
 
 size_t G1PLABAllocator::waste() const {
   size_t result = 0;
-  for (uint state = 0; state < InCSetState::Num; state++) {
+  for (uint state = 0; state < G1HeapRegionAttr::Num; state++) {
     PLAB * const buf = _alloc_buffers[state];
     if (buf != NULL) {
       result += buf->waste();
@@ -334,7 +334,7 @@
 
 size_t G1PLABAllocator::undo_waste() const {
   size_t result = 0;
-  for (uint state = 0; state < InCSetState::Num; state++) {
+  for (uint state = 0; state < G1HeapRegionAttr::Num; state++) {
     PLAB * const buf = _alloc_buffers[state];
     if (buf != NULL) {
       result += buf->undo_waste();
--- a/src/hotspot/share/gc/g1/g1Allocator.hpp	Tue May 14 08:52:51 2019 -0400
+++ b/src/hotspot/share/gc/g1/g1Allocator.hpp	Tue May 14 15:36:26 2019 +0200
@@ -26,7 +26,7 @@
 #define SHARE_GC_G1_G1ALLOCATOR_HPP
 
 #include "gc/g1/g1AllocRegion.hpp"
-#include "gc/g1/g1InCSetState.hpp"
+#include "gc/g1/g1HeapRegionAttr.hpp"
 #include "gc/shared/collectedHeap.hpp"
 #include "gc/shared/plab.hpp"
 
@@ -112,10 +112,10 @@
   // allocation region, either by picking one or expanding the
   // heap, and then allocate a block of the given size. The block
   // may not be a humongous - it must fit into a single heap region.
-  HeapWord* par_allocate_during_gc(InCSetState dest,
+  HeapWord* par_allocate_during_gc(G1HeapRegionAttr dest,
                                    size_t word_size);
 
-  HeapWord* par_allocate_during_gc(InCSetState dest,
+  HeapWord* par_allocate_during_gc(G1HeapRegionAttr dest,
                                    size_t min_word_size,
                                    size_t desired_word_size,
                                    size_t* actual_word_size);
@@ -132,7 +132,7 @@
 
   PLAB  _surviving_alloc_buffer;
   PLAB  _tenured_alloc_buffer;
-  PLAB* _alloc_buffers[InCSetState::Num];
+  PLAB* _alloc_buffers[G1HeapRegionAttr::Num];
 
   // The survivor alignment in effect in bytes.
   // == 0 : don't align survivors
@@ -142,10 +142,10 @@
   const uint _survivor_alignment_bytes;
 
   // Number of words allocated directly (not counting PLAB allocation).
-  size_t _direct_allocated[InCSetState::Num];
+  size_t _direct_allocated[G1HeapRegionAttr::Num];
 
   void flush_and_retire_stats();
-  inline PLAB* alloc_buffer(InCSetState dest);
+  inline PLAB* alloc_buffer(G1HeapRegionAttr dest);
 
   // Calculate the survivor space object alignment in bytes. Returns that or 0 if
   // there are no restrictions on survivor alignment.
@@ -162,20 +162,20 @@
   // allocating a new PLAB. Returns the address of the allocated memory, NULL if
   // not successful. Plab_refill_failed indicates whether an attempt to refill the
   // PLAB failed or not.
-  HeapWord* allocate_direct_or_new_plab(InCSetState dest,
+  HeapWord* allocate_direct_or_new_plab(G1HeapRegionAttr dest,
                                         size_t word_sz,
                                         bool* plab_refill_failed);
 
   // Allocate word_sz words in the PLAB of dest.  Returns the address of the
   // allocated memory, NULL if not successful.
-  inline HeapWord* plab_allocate(InCSetState dest,
+  inline HeapWord* plab_allocate(G1HeapRegionAttr dest,
                                  size_t word_sz);
 
-  inline HeapWord* allocate(InCSetState dest,
+  inline HeapWord* allocate(G1HeapRegionAttr dest,
                             size_t word_sz,
                             bool* refill_failed);
 
-  void undo_allocation(InCSetState dest, HeapWord* obj, size_t word_sz);
+  void undo_allocation(G1HeapRegionAttr dest, HeapWord* obj, size_t word_sz);
 };
 
 // G1ArchiveRegionMap is a boolean array used to mark G1 regions as
--- a/src/hotspot/share/gc/g1/g1Allocator.inline.hpp	Tue May 14 08:52:51 2019 -0400
+++ b/src/hotspot/share/gc/g1/g1Allocator.inline.hpp	Tue May 14 15:36:26 2019 +0200
@@ -63,15 +63,15 @@
   return mutator_alloc_region()->attempt_allocation_force(word_size);
 }
 
-inline PLAB* G1PLABAllocator::alloc_buffer(InCSetState dest) {
+inline PLAB* G1PLABAllocator::alloc_buffer(G1HeapRegionAttr dest) {
   assert(dest.is_valid(),
-         "Allocation buffer index out of bounds: " CSETSTATE_FORMAT, dest.value());
-  assert(_alloc_buffers[dest.value()] != NULL,
-         "Allocation buffer is NULL: " CSETSTATE_FORMAT, dest.value());
-  return _alloc_buffers[dest.value()];
+         "Allocation buffer index out of bounds: %s", dest.get_type_str());
+  assert(_alloc_buffers[dest.type()] != NULL,
+         "Allocation buffer is NULL: %s", dest.get_type_str());
+  return _alloc_buffers[dest.type()];
 }
 
-inline HeapWord* G1PLABAllocator::plab_allocate(InCSetState dest,
+inline HeapWord* G1PLABAllocator::plab_allocate(G1HeapRegionAttr dest,
                                                 size_t word_sz) {
   PLAB* buffer = alloc_buffer(dest);
   if (_survivor_alignment_bytes == 0 || !dest.is_young()) {
@@ -81,7 +81,7 @@
   }
 }
 
-inline HeapWord* G1PLABAllocator::allocate(InCSetState dest,
+inline HeapWord* G1PLABAllocator::allocate(G1HeapRegionAttr dest,
                                            size_t word_sz,
                                            bool* refill_failed) {
   HeapWord* const obj = plab_allocate(dest, word_sz);
--- a/src/hotspot/share/gc/g1/g1BiasedArray.hpp	Tue May 14 08:52:51 2019 -0400
+++ b/src/hotspot/share/gc/g1/g1BiasedArray.hpp	Tue May 14 15:36:26 2019 +0200
@@ -128,6 +128,11 @@
     return biased_base()[biased_index];
   }
 
+  T* get_ref_by_index(uintptr_t index) const {
+    verify_index(index);
+    return &this->base()[index];
+  }
+
   // Return the index of the element of the given array that covers the given
   // word in the heap.
   idx_t get_index_by_address(HeapWord* value) const {
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp	Tue May 14 08:52:51 2019 -0400
+++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp	Tue May 14 15:36:26 2019 +0200
@@ -1536,7 +1536,7 @@
   _ref_processor_cm(NULL),
   _is_alive_closure_cm(this),
   _is_subject_to_discovery_cm(this),
-  _in_cset_fast_test() {
+  _region_attr() {
 
   _verifier = new G1HeapVerifier(this);
 
@@ -1772,7 +1772,7 @@
     HeapWord* end = _hrm->reserved().end();
     size_t granularity = HeapRegion::GrainBytes;
 
-    _in_cset_fast_test.initialize(start, end, granularity);
+    _region_attr.initialize(start, end, granularity);
     _humongous_reclaim_candidates.initialize(start, end, granularity);
   }
 
@@ -2626,7 +2626,7 @@
          G1EagerReclaimHumongousObjects && rem_set->is_empty();
 }
 
-class RegisterHumongousWithInCSetFastTestClosure : public HeapRegionClosure {
+class RegisterRegionsWithRegionAttrTableClosure : public HeapRegionClosure {
  private:
   size_t _total_humongous;
   size_t _candidate_humongous;
@@ -2690,24 +2690,26 @@
   }
 
  public:
-  RegisterHumongousWithInCSetFastTestClosure()
+  RegisterRegionsWithRegionAttrTableClosure()
   : _total_humongous(0),
     _candidate_humongous(0),
     _dcq(&G1BarrierSet::dirty_card_queue_set()) {
   }
 
   virtual bool do_heap_region(HeapRegion* r) {
+    G1CollectedHeap* g1h = G1CollectedHeap::heap();
+
     if (!r->is_starts_humongous()) {
+      g1h->register_region_with_region_attr(r);
       return false;
     }
-    G1CollectedHeap* g1h = G1CollectedHeap::heap();
 
     bool is_candidate = humongous_region_is_candidate(g1h, r);
     uint rindex = r->hrm_index();
     g1h->set_humongous_reclaim_candidate(rindex, is_candidate);
     if (is_candidate) {
       _candidate_humongous++;
-      g1h->register_humongous_region_with_cset(rindex);
+      g1h->register_humongous_region_with_region_attr(rindex);
       // Is_candidate already filters out humongous object with large remembered sets.
       // If we have a humongous object with a few remembered sets, we simply flush these
       // remembered set entries into the DCQS. That will result in automatic
@@ -2743,8 +2745,14 @@
         // collecting remembered set entries for humongous regions that were not
         // reclaimed.
         r->rem_set()->set_state_complete();
+#ifdef ASSERT
+        G1HeapRegionAttr region_attr = g1h->region_attr(oop(r->bottom()));
+        assert(region_attr.needs_remset_update(), "must be");
+#endif
       }
       assert(r->rem_set()->is_empty(), "At this point any humongous candidate remembered set must be empty.");
+    } else {
+      g1h->register_region_with_region_attr(r);
     }
     _total_humongous++;
 
@@ -2757,21 +2765,15 @@
   void flush_rem_set_entries() { _dcq.flush(); }
 };
 
-void G1CollectedHeap::register_humongous_regions_with_cset() {
-  if (!G1EagerReclaimHumongousObjects) {
-    phase_times()->record_fast_reclaim_humongous_stats(0.0, 0, 0);
-    return;
-  }
-  double time = os::elapsed_counter();
-
-  // Collect reclaim candidate information and register candidates with cset.
-  RegisterHumongousWithInCSetFastTestClosure cl;
+void G1CollectedHeap::register_regions_with_region_attr() {
+  Ticks start = Ticks::now();
+
+  RegisterRegionsWithRegionAttrTableClosure cl;
   heap_region_iterate(&cl);
 
-  time = ((double)(os::elapsed_counter() - time) / os::elapsed_frequency()) * 1000.0;
-  phase_times()->record_fast_reclaim_humongous_stats(time,
-                                                     cl.total_humongous(),
-                                                     cl.candidate_humongous());
+  phase_times()->record_register_regions((Ticks::now() - start).seconds() * 1000.0,
+                                         cl.total_humongous(),
+                                         cl.candidate_humongous());
   _has_humongous_reclaim_candidates = cl.candidate_humongous() > 0;
 
   // Finally flush all remembered set entries to re-check into the global DCQS.
@@ -2861,7 +2863,7 @@
 
   collection_set()->start_incremental_building();
 
-  clear_cset_fast_test();
+  clear_region_attr();
 
   guarantee(_eden.length() == 0, "eden should have been cleared");
   policy()->transfer_survivors_to_cset(survivor());
@@ -3302,17 +3304,17 @@
     oop obj = *p;
     assert(obj != NULL, "the caller should have filtered out NULL values");
 
-    const InCSetState cset_state =_g1h->in_cset_state(obj);
-    if (!cset_state.is_in_cset_or_humongous()) {
+    const G1HeapRegionAttr region_attr =_g1h->region_attr(obj);
+    if (!region_attr.is_in_cset_or_humongous()) {
       return;
     }
-    if (cset_state.is_in_cset()) {
+    if (region_attr.is_in_cset()) {
       assert( obj->is_forwarded(), "invariant" );
       *p = obj->forwardee();
     } else {
       assert(!obj->is_forwarded(), "invariant" );
-      assert(cset_state.is_humongous(),
-             "Only allowed InCSet state is IsHumongous, but is %d", cset_state.value());
+      assert(region_attr.is_humongous(),
+             "Only allowed G1HeapRegionAttr state is IsHumongous, but is %d", region_attr.type());
      _g1h->set_humongous_is_live(obj);
     }
   }
@@ -3572,7 +3574,7 @@
   // Initialize the GC alloc regions.
   _allocator->init_gc_alloc_regions(evacuation_info);
 
-  register_humongous_regions_with_cset();
+  register_regions_with_region_attr();
   assert(_verifier->check_cset_fast_test(), "Inconsistency in the InCSetState table.");
 
   rem_set()->prepare_for_oops_into_collection_set_do();
@@ -3970,7 +3972,7 @@
       G1CollectedHeap* g1h = G1CollectedHeap::heap();
 
       assert(r->in_collection_set(), "Region %u should be in collection set.", r->hrm_index());
-      g1h->clear_in_cset(r);
+      g1h->clear_region_attr(r);
 
       if (r->is_young()) {
         assert(r->young_index_in_cset() != -1 && (uint)r->young_index_in_cset() < g1h->collection_set()->young_region_length(),
@@ -4031,7 +4033,7 @@
       G1Policy* policy = g1h->policy();
       policy->add_bytes_allocated_in_old_since_last_gc(_bytes_allocated_in_old_since_last_gc);
 
-      g1h->alloc_buffer_stats(InCSetState::Old)->add_failure_used_and_waste(_failure_used_words, _failure_waste_words);
+      g1h->alloc_buffer_stats(G1HeapRegionAttr::Old)->add_failure_used_and_waste(_failure_used_words, _failure_waste_words);
     }
   };
 
@@ -4365,7 +4367,7 @@
 public:
   virtual bool do_heap_region(HeapRegion* r) {
     assert(r->in_collection_set(), "Region %u must have been in collection set", r->hrm_index());
-    G1CollectedHeap::heap()->clear_in_cset(r);
+    G1CollectedHeap::heap()->clear_region_attr(r);
     r->set_young_index_in_cset(-1);
     return false;
   }
@@ -4582,7 +4584,7 @@
 
 // Methods for the GC alloc regions
 
-bool G1CollectedHeap::has_more_regions(InCSetState dest) {
+bool G1CollectedHeap::has_more_regions(G1HeapRegionAttr dest) {
   if (dest.is_old()) {
     return true;
   } else {
@@ -4590,7 +4592,7 @@
   }
 }
 
-HeapRegion* G1CollectedHeap::new_gc_alloc_region(size_t word_size, InCSetState dest) {
+HeapRegion* G1CollectedHeap::new_gc_alloc_region(size_t word_size, G1HeapRegionAttr dest) {
   assert(FreeList_lock->owned_by_self(), "pre-condition");
 
   if (!has_more_regions(dest)) {
@@ -4618,6 +4620,7 @@
       _verifier->check_bitmaps("Old Region Allocation", new_alloc_region);
     }
     _policy->remset_tracker()->update_at_allocate(new_alloc_region);
+    register_region_with_region_attr(new_alloc_region);
     _hr_printer.alloc(new_alloc_region);
     return new_alloc_region;
   }
@@ -4626,12 +4629,12 @@
 
 void G1CollectedHeap::retire_gc_alloc_region(HeapRegion* alloc_region,
                                              size_t allocated_bytes,
-                                             InCSetState dest) {
+                                             G1HeapRegionAttr dest) {
   policy()->record_bytes_copied_during_gc(allocated_bytes);
   if (dest.is_old()) {
     old_set_add(alloc_region);
   } else {
-    assert(dest.is_young(), "Retiring alloc region should be young(%d)", dest.value());
+    assert(dest.is_young(), "Retiring alloc region should be young (%d)", dest.type());
     _survivor.add_used_bytes(allocated_bytes);
   }
 
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp	Tue May 14 08:52:51 2019 -0400
+++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp	Tue May 14 15:36:26 2019 +0200
@@ -40,7 +40,7 @@
 #include "gc/g1/g1HeapTransition.hpp"
 #include "gc/g1/g1HeapVerifier.hpp"
 #include "gc/g1/g1HRPrinter.hpp"
-#include "gc/g1/g1InCSetState.hpp"
+#include "gc/g1/g1HeapRegionAttr.hpp"
 #include "gc/g1/g1MonitoringSupport.hpp"
 #include "gc/g1/g1SurvivorRegions.hpp"
 #include "gc/g1/g1YCTypes.hpp"
@@ -464,10 +464,10 @@
                                    size_t allocated_bytes);
 
   // For GC alloc regions.
-  bool has_more_regions(InCSetState dest);
-  HeapRegion* new_gc_alloc_region(size_t word_size, InCSetState dest);
+  bool has_more_regions(G1HeapRegionAttr dest);
+  HeapRegion* new_gc_alloc_region(size_t word_size, G1HeapRegionAttr dest);
   void retire_gc_alloc_region(HeapRegion* alloc_region,
-                              size_t allocated_bytes, InCSetState dest);
+                              size_t allocated_bytes, G1HeapRegionAttr dest);
 
   // - if explicit_gc is true, the GC is for a System.gc() etc,
   //   otherwise it's for a failed allocation.
@@ -551,10 +551,10 @@
   bool expand(size_t expand_bytes, WorkGang* pretouch_workers = NULL, double* expand_time_ms = NULL);
 
   // Returns the PLAB statistics for a given destination.
-  inline G1EvacStats* alloc_buffer_stats(InCSetState dest);
+  inline G1EvacStats* alloc_buffer_stats(G1HeapRegionAttr dest);
 
   // Determines PLAB size for a given destination.
-  inline size_t desired_plab_sz(InCSetState dest);
+  inline size_t desired_plab_sz(G1HeapRegionAttr dest);
 
   // Do anything common to GC's.
   void gc_prologue(bool full);
@@ -573,27 +573,24 @@
   inline void set_humongous_is_live(oop obj);
 
   // Register the given region to be part of the collection set.
-  inline void register_humongous_region_with_cset(uint index);
-  // Register regions with humongous objects (actually on the start region) in
-  // the in_cset_fast_test table.
-  void register_humongous_regions_with_cset();
+  inline void register_humongous_region_with_region_attr(uint index);
+  // Update region attributes table with information about all regions.
+  void register_regions_with_region_attr();
   // We register a region with the fast "in collection set" test. We
   // simply set to true the array slot corresponding to this region.
-  void register_young_region_with_cset(HeapRegion* r) {
-    _in_cset_fast_test.set_in_young(r->hrm_index());
-  }
-  void register_old_region_with_cset(HeapRegion* r) {
-    _in_cset_fast_test.set_in_old(r->hrm_index());
+  void register_young_region_with_region_attr(HeapRegion* r) {
+    _region_attr.set_in_young(r->hrm_index());
   }
-  void register_optional_region_with_cset(HeapRegion* r) {
-    _in_cset_fast_test.set_optional(r->hrm_index());
-  }
-  void clear_in_cset(const HeapRegion* hr) {
-    _in_cset_fast_test.clear(hr);
+  inline void register_region_with_region_attr(HeapRegion* r);
+  inline void register_old_region_with_region_attr(HeapRegion* r);
+  inline void register_optional_region_with_region_attr(HeapRegion* r);
+
+  void clear_region_attr(const HeapRegion* hr) {
+    _region_attr.clear(hr);
   }
 
-  void clear_cset_fast_test() {
-    _in_cset_fast_test.clear();
+  void clear_region_attr() {
+    _region_attr.clear();
   }
 
   bool is_user_requested_concurrent_full_gc(GCCause::Cause cause);
@@ -1110,11 +1107,11 @@
   // This array is used for a quick test on whether a reference points into
   // the collection set or not. Each of the array's elements denotes whether the
   // corresponding region is in the collection set or not.
-  G1InCSetStateFastTestBiasedMappedArray _in_cset_fast_test;
+  G1HeapRegionAttrBiasedMappedArray _region_attr;
 
  public:
 
-  inline InCSetState in_cset_state(const oop obj);
+  inline G1HeapRegionAttr region_attr(const oop obj);
 
   // Return "TRUE" iff the given object address is in the reserved
   // region of g1.
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp	Tue May 14 08:52:51 2019 -0400
+++ b/src/hotspot/share/gc/g1/g1CollectedHeap.inline.hpp	Tue May 14 15:36:26 2019 +0200
@@ -30,6 +30,7 @@
 #include "gc/g1/g1CollectorState.hpp"
 #include "gc/g1/g1Policy.hpp"
 #include "gc/g1/heapRegionManager.inline.hpp"
+#include "gc/g1/heapRegionRemSet.hpp"
 #include "gc/g1/heapRegionSet.inline.hpp"
 #include "gc/shared/taskqueue.inline.hpp"
 #include "runtime/orderAccess.hpp"
@@ -38,11 +39,11 @@
   return _policy->phase_times();
 }
 
-G1EvacStats* G1CollectedHeap::alloc_buffer_stats(InCSetState dest) {
-  switch (dest.value()) {
-    case InCSetState::Young:
+G1EvacStats* G1CollectedHeap::alloc_buffer_stats(G1HeapRegionAttr dest) {
+  switch (dest.type()) {
+    case G1HeapRegionAttr::Young:
       return &_survivor_evac_stats;
-    case InCSetState::Old:
+    case G1HeapRegionAttr::Old:
       return &_old_evac_stats;
     default:
       ShouldNotReachHere();
@@ -50,7 +51,7 @@
   }
 }
 
-size_t G1CollectedHeap::desired_plab_sz(InCSetState dest) {
+size_t G1CollectedHeap::desired_plab_sz(G1HeapRegionAttr dest) {
   size_t gclab_word_size = alloc_buffer_stats(dest)->desired_plab_sz(workers()->active_workers());
   // Prevent humongous PLAB sizes for two reasons:
   // * PLABs are allocated using a similar paths as oops, but should
@@ -150,23 +151,35 @@
 }
 
 inline bool G1CollectedHeap::is_in_cset(HeapWord* addr) {
-  return _in_cset_fast_test.is_in_cset(addr);
+  return _region_attr.is_in_cset(addr);
 }
 
 bool G1CollectedHeap::is_in_cset(const HeapRegion* hr) {
-  return _in_cset_fast_test.is_in_cset(hr);
+  return _region_attr.is_in_cset(hr);
 }
 
 bool G1CollectedHeap::is_in_cset_or_humongous(const oop obj) {
-  return _in_cset_fast_test.is_in_cset_or_humongous((HeapWord*)obj);
+  return _region_attr.is_in_cset_or_humongous((HeapWord*)obj);
+}
+
+G1HeapRegionAttr G1CollectedHeap::region_attr(const oop obj) {
+  return _region_attr.at((HeapWord*)obj);
+}
+
+void G1CollectedHeap::register_humongous_region_with_region_attr(uint index) {
+  _region_attr.set_humongous(index, region_at(index)->rem_set()->is_tracked());
 }
 
-InCSetState G1CollectedHeap::in_cset_state(const oop obj) {
-  return _in_cset_fast_test.at((HeapWord*)obj);
+void G1CollectedHeap::register_region_with_region_attr(HeapRegion* r) {
+  _region_attr.set_has_remset(r->hrm_index(), r->rem_set()->is_tracked());
 }
 
-void G1CollectedHeap::register_humongous_region_with_cset(uint index) {
-  _in_cset_fast_test.set_humongous(index);
+void G1CollectedHeap::register_old_region_with_region_attr(HeapRegion* r) {
+  _region_attr.set_in_old(r->hrm_index(), r->rem_set()->is_tracked());
+}
+
+void G1CollectedHeap::register_optional_region_with_region_attr(HeapRegion* r) {
+  _region_attr.set_optional(r->hrm_index(), r->rem_set()->is_tracked());
 }
 
 #ifndef PRODUCT
@@ -294,7 +307,7 @@
   // thread (i.e. within the VM thread).
   if (is_humongous_reclaim_candidate(region)) {
     set_humongous_reclaim_candidate(region, false);
-    _in_cset_fast_test.clear_humongous(region);
+    _region_attr.clear_humongous(region);
   }
 }
 
--- a/src/hotspot/share/gc/g1/g1CollectionSet.cpp	Tue May 14 08:52:51 2019 -0400
+++ b/src/hotspot/share/gc/g1/g1CollectionSet.cpp	Tue May 14 15:36:26 2019 +0200
@@ -121,7 +121,7 @@
   assert(hr->is_old(), "the region should be old");
 
   assert(!hr->in_collection_set(), "should not already be in the collection set");
-  _g1h->register_old_region_with_cset(hr);
+  _g1h->register_old_region_with_region_attr(hr);
 
   _collection_set_regions[_collection_set_cur_length++] = hr->hrm_index();
   assert(_collection_set_cur_length <= _collection_set_max_length, "Collection set now larger than maximum size.");
@@ -137,7 +137,7 @@
   assert(hr->is_old(), "the region should be old");
   assert(!hr->in_collection_set(), "should not already be in the CSet");
 
-  _g1h->register_optional_region_with_cset(hr);
+  _g1h->register_optional_region_with_region_attr(hr);
 
   hr->set_index_in_opt_cset(_num_optional_regions++);
 }
@@ -316,7 +316,7 @@
   }
 
   assert(!hr->in_collection_set(), "invariant");
-  _g1h->register_young_region_with_cset(hr);
+  _g1h->register_young_region_with_region_attr(hr);
 }
 
 void G1CollectionSet::add_survivor_regions(HeapRegion* hr) {
@@ -492,7 +492,7 @@
     HeapRegion* r = candidates()->at(candidate_idx + i);
     // This potentially optional candidate region is going to be an actual collection
     // set region. Clear cset marker.
-    _g1h->clear_in_cset(r);
+    _g1h->clear_region_attr(r);
     add_old_region(r);
   }
   candidates()->remove(num_old_candidate_regions);
@@ -526,7 +526,7 @@
   for (uint i = 0; i < _num_optional_regions; i++) {
     HeapRegion* r = candidates()->at(candidates()->cur_idx() + i);
     pss->record_unused_optional_region(r);
-    _g1h->clear_in_cset(r);
+    _g1h->clear_region_attr(r);
     r->clear_index_in_opt_cset();
   }
   free_optional_regions();
--- a/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp	Tue May 14 08:52:51 2019 -0400
+++ b/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp	Tue May 14 15:36:26 2019 +0200
@@ -170,7 +170,7 @@
   _recorded_total_free_cset_time_ms = 0.0;
   _recorded_serial_free_cset_time_ms = 0.0;
   _cur_fast_reclaim_humongous_time_ms = 0.0;
-  _cur_fast_reclaim_humongous_register_time_ms = 0.0;
+  _cur_region_register_time = 0.0;
   _cur_fast_reclaim_humongous_total = 0;
   _cur_fast_reclaim_humongous_candidates = 0;
   _cur_fast_reclaim_humongous_reclaimed = 0;
@@ -364,7 +364,7 @@
   const double sum_ms = _root_region_scan_wait_time_ms +
                         _recorded_young_cset_choice_time_ms +
                         _recorded_non_young_cset_choice_time_ms +
-                        _cur_fast_reclaim_humongous_register_time_ms +
+                        _cur_region_register_time +
                         _recorded_clear_claimed_marks_time_ms;
 
   info_time("Pre Evacuate Collection Set", sum_ms);
@@ -374,8 +374,8 @@
   }
   debug_time("Prepare TLABs", _cur_prepare_tlab_time_ms);
   debug_time("Choose Collection Set", (_recorded_young_cset_choice_time_ms + _recorded_non_young_cset_choice_time_ms));
+  debug_time("Region Register", _cur_region_register_time);
   if (G1EagerReclaimHumongousObjects) {
-    debug_time("Humongous Register", _cur_fast_reclaim_humongous_register_time_ms);
     trace_count("Humongous Total", _cur_fast_reclaim_humongous_total);
     trace_count("Humongous Candidate", _cur_fast_reclaim_humongous_candidates);
   }
--- a/src/hotspot/share/gc/g1/g1GCPhaseTimes.hpp	Tue May 14 08:52:51 2019 -0400
+++ b/src/hotspot/share/gc/g1/g1GCPhaseTimes.hpp	Tue May 14 15:36:26 2019 +0200
@@ -176,8 +176,9 @@
 
   double _recorded_serial_free_cset_time_ms;
 
+  double _cur_region_register_time;
+
   double _cur_fast_reclaim_humongous_time_ms;
-  double _cur_fast_reclaim_humongous_register_time_ms;
   size_t _cur_fast_reclaim_humongous_total;
   size_t _cur_fast_reclaim_humongous_candidates;
   size_t _cur_fast_reclaim_humongous_reclaimed;
@@ -305,8 +306,8 @@
     _recorded_serial_free_cset_time_ms = time_ms;
   }
 
-  void record_fast_reclaim_humongous_stats(double time_ms, size_t total, size_t candidates) {
-    _cur_fast_reclaim_humongous_register_time_ms = time_ms;
+  void record_register_regions(double time_ms, size_t total, size_t candidates) {
+    _cur_region_register_time = time_ms;
     _cur_fast_reclaim_humongous_total = total;
     _cur_fast_reclaim_humongous_candidates = candidates;
   }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/gc/g1/g1HeapRegionAttr.hpp	Tue May 14 15:36:26 2019 +0200
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+#ifndef SHARE_GC_G1_G1HEAPREGIONATTR_HPP
+#define SHARE_GC_G1_G1HEAPREGIONATTR_HPP
+
+#include "gc/g1/g1BiasedArray.hpp"
+#include "gc/g1/heapRegion.hpp"
+
+// Per-region attributes often used during garbage collection to avoid costly
+// lookups for that information all over the place.
+struct G1HeapRegionAttr {
+public:
+  // We use different types to represent the state value depending on platform as
+  // some have issues loading parts of words.
+#ifdef SPARC
+  typedef int32_t region_type_t;
+  typedef uint32_t needs_remset_update_t;
+#else
+  typedef int8_t region_type_t;
+  typedef uint8_t needs_remset_update_t;
+#endif
+
+private:
+  needs_remset_update_t _needs_remset_update;
+  region_type_t _type;
+
+public:
+  // Selection of the values for the _type field were driven to micro-optimize the
+  // encoding and frequency of the checks.
+  // The most common check for a given reference is whether the region is in the
+  // collection set or not, and which generation this region is in.
+  // The selected encoding allows us to use a single check (> NotInCSet) for the
+  // former.
+  //
+  // The other values are used for objects requiring various special cases,
+  // for example eager reclamation of humongous objects or optional regions.
+  static const region_type_t Optional     =  -2;    // The region is optional and NOT in the current collection set.
+  static const region_type_t Humongous    =  -1;    // The region is a humongous candidate not in the current collection set.
+  static const region_type_t NotInCSet    =   0;    // The region is not in the collection set.
+  static const region_type_t Young        =   1;    // The region is in the collection set and a young region.
+  static const region_type_t Old          =   2;    // The region is in the collection set and an old region.
+  static const region_type_t Num          =   3;
+
+  G1HeapRegionAttr(region_type_t type = NotInCSet, bool needs_remset_update = false) :
+    _needs_remset_update(needs_remset_update), _type(type) {
+
+    assert(is_valid(), "Invalid type %d", _type);
+  }
+
+  region_type_t type() const           { return _type; }
+
+  const char* get_type_str() const {
+    switch (type()) {
+      case Optional: return "Optional";
+      case Humongous: return "Humongous";
+      case NotInCSet: return "NotInCSet";
+      case Young: return "Young";
+      case Old: return "Old";
+      default: ShouldNotReachHere(); return "";
+    }
+  }
+
+  bool needs_remset_update() const     { return _needs_remset_update != 0; }
+
+  void set_old()                       { _type = Old; }
+  void clear_humongous()               {
+    assert(is_humongous() || !is_in_cset(), "must be");
+    _type = NotInCSet;
+  }
+  void set_has_remset(bool value)      { _needs_remset_update = value ? 1 : 0; }
+
+  bool is_in_cset_or_humongous() const { return is_in_cset() || is_humongous(); }
+  bool is_in_cset() const              { return type() > NotInCSet; }
+
+  bool is_humongous() const            { return type() == Humongous; }
+  bool is_young() const                { return type() == Young; }
+  bool is_old() const                  { return type() == Old; }
+  bool is_optional() const             { return type() == Optional; }
+
+#ifdef ASSERT
+  bool is_default() const              { return type() == NotInCSet; }
+  bool is_valid() const                { return (type() >= Optional && type() < Num); }
+  bool is_valid_gen() const            { return (type() >= Young && type() <= Old); }
+#endif
+};
+
+// Table for all regions in the heap for above.
+//
+// We use this to speed up reference processing during young collection and
+// quickly reclaim humongous objects. For the latter, at the start of GC, by adding
+// it as a humongous region we enable special handling for that region. During the
+// reference iteration closures, when we see a humongous region, we then simply mark
+// it as referenced, i.e. live, and remove it from this table to prevent further
+// processing on it.
+//
+// This means that this does NOT completely correspond to the information stored
+// in a HeapRegion, but only to what is interesting for the current young collection.
+class G1HeapRegionAttrBiasedMappedArray : public G1BiasedMappedArray<G1HeapRegionAttr> {
+ protected:
+  G1HeapRegionAttr default_value() const { return G1HeapRegionAttr(G1HeapRegionAttr::NotInCSet); }
+ public:
+  void set_optional(uintptr_t index, bool needs_remset_update) {
+    assert(get_by_index(index).is_default(),
+           "Region attributes at index " INTPTR_FORMAT " should be default but is %s", index, get_by_index(index).get_type_str());
+    set_by_index(index, G1HeapRegionAttr(G1HeapRegionAttr::Optional, needs_remset_update));
+  }
+
+  void set_humongous(uintptr_t index, bool needs_remset_update) {
+    assert(get_by_index(index).is_default(),
+           "Region attributes at index " INTPTR_FORMAT " should be default but is %s", index, get_by_index(index).get_type_str());
+    set_by_index(index, G1HeapRegionAttr(G1HeapRegionAttr::Humongous, needs_remset_update));
+  }
+
+  void clear_humongous(uintptr_t index) {
+    get_ref_by_index(index)->clear_humongous();
+  }
+
+  void set_has_remset(uintptr_t index, bool needs_remset_update) {
+    get_ref_by_index(index)->set_has_remset(needs_remset_update);
+  }
+
+  void set_in_young(uintptr_t index) {
+    assert(get_by_index(index).is_default(),
+           "Region attributes at index " INTPTR_FORMAT " should be default but is %s", index, get_by_index(index).get_type_str());
+    set_by_index(index, G1HeapRegionAttr(G1HeapRegionAttr::Young, true));
+  }
+
+  void set_in_old(uintptr_t index, bool needs_remset_update) {
+    assert(get_by_index(index).is_default(),
+           "Region attributes at index " INTPTR_FORMAT " should be default but is %s", index, get_by_index(index).get_type_str());
+    set_by_index(index, G1HeapRegionAttr(G1HeapRegionAttr::Old, needs_remset_update));
+  }
+
+  bool is_in_cset_or_humongous(HeapWord* addr) const { return at(addr).is_in_cset_or_humongous(); }
+  bool is_in_cset(HeapWord* addr) const { return at(addr).is_in_cset(); }
+  bool is_in_cset(const HeapRegion* hr) const { return get_by_index(hr->hrm_index()).is_in_cset(); }
+  G1HeapRegionAttr at(HeapWord* addr) const { return get_by_address(addr); }
+  void clear() { G1BiasedMappedArray<G1HeapRegionAttr>::clear(); }
+  void clear(const HeapRegion* hr) { return set_by_index(hr->hrm_index(), G1HeapRegionAttr(G1HeapRegionAttr::NotInCSet)); }
+};
+
+#endif // SHARE_GC_G1_G1HEAPREGIONATTR_HPP
--- a/src/hotspot/share/gc/g1/g1HeapVerifier.cpp	Tue May 14 08:52:51 2019 -0400
+++ b/src/hotspot/share/gc/g1/g1HeapVerifier.cpp	Tue May 14 15:36:26 2019 +0200
@@ -790,50 +790,50 @@
 
   virtual bool do_heap_region(HeapRegion* hr) {
     uint i = hr->hrm_index();
-    InCSetState cset_state = (InCSetState) G1CollectedHeap::heap()->_in_cset_fast_test.get_by_index(i);
+    G1HeapRegionAttr region_attr = (G1HeapRegionAttr) G1CollectedHeap::heap()->_region_attr.get_by_index(i);
     if (hr->is_humongous()) {
       if (hr->in_collection_set()) {
         log_error(gc, verify)("## humongous region %u in CSet", i);
         _failures = true;
         return true;
       }
-      if (cset_state.is_in_cset()) {
-        log_error(gc, verify)("## inconsistent cset state " CSETSTATE_FORMAT " for humongous region %u", cset_state.value(), i);
+      if (region_attr.is_in_cset()) {
+        log_error(gc, verify)("## inconsistent region attr type %s for humongous region %u", region_attr.get_type_str(), i);
         _failures = true;
         return true;
       }
-      if (hr->is_continues_humongous() && cset_state.is_humongous()) {
-        log_error(gc, verify)("## inconsistent cset state " CSETSTATE_FORMAT " for continues humongous region %u", cset_state.value(), i);
+      if (hr->is_continues_humongous() && region_attr.is_humongous()) {
+        log_error(gc, verify)("## inconsistent region attr type %s for continues humongous region %u", region_attr.get_type_str(), i);
         _failures = true;
         return true;
       }
     } else {
-      if (cset_state.is_humongous()) {
-        log_error(gc, verify)("## inconsistent cset state " CSETSTATE_FORMAT " for non-humongous region %u", cset_state.value(), i);
+      if (region_attr.is_humongous()) {
+        log_error(gc, verify)("## inconsistent region attr type %s for non-humongous region %u", region_attr.get_type_str(), i);
         _failures = true;
         return true;
       }
-      if (hr->in_collection_set() != cset_state.is_in_cset()) {
-        log_error(gc, verify)("## in CSet %d / cset state " CSETSTATE_FORMAT " inconsistency for region %u",
-                             hr->in_collection_set(), cset_state.value(), i);
+      if (hr->in_collection_set() != region_attr.is_in_cset()) {
+        log_error(gc, verify)("## in CSet %d / region attr type %s inconsistency for region %u",
+                             hr->in_collection_set(), region_attr.get_type_str(), i);
         _failures = true;
         return true;
       }
-      if (cset_state.is_in_cset()) {
+      if (region_attr.is_in_cset()) {
         if (hr->is_archive()) {
           log_error(gc, verify)("## is_archive in collection set for region %u", i);
           _failures = true;
           return true;
         }
-        if (hr->is_young() != (cset_state.is_young())) {
-          log_error(gc, verify)("## is_young %d / cset state " CSETSTATE_FORMAT " inconsistency for region %u",
-                               hr->is_young(), cset_state.value(), i);
+        if (hr->is_young() != (region_attr.is_young())) {
+          log_error(gc, verify)("## is_young %d / region attr type %s inconsistency for region %u",
+                               hr->is_young(), region_attr.get_type_str(), i);
           _failures = true;
           return true;
         }
-        if (hr->is_old() != (cset_state.is_old())) {
-          log_error(gc, verify)("## is_old %d / cset state " CSETSTATE_FORMAT " inconsistency for region %u",
-                               hr->is_old(), cset_state.value(), i);
+        if (hr->is_old() != (region_attr.is_old())) {
+          log_error(gc, verify)("## is_old %d / region attr type %s inconsistency for region %u",
+                               hr->is_old(), region_attr.get_type_str(), i);
           _failures = true;
           return true;
         }
--- a/src/hotspot/share/gc/g1/g1InCSetState.hpp	Tue May 14 08:52:51 2019 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,142 +0,0 @@
-/*
- * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- *
- */
-
-#ifndef SHARE_GC_G1_G1INCSETSTATE_HPP
-#define SHARE_GC_G1_G1INCSETSTATE_HPP
-
-#include "gc/g1/g1BiasedArray.hpp"
-#include "gc/g1/heapRegion.hpp"
-
-// Per-region state during garbage collection.
-struct InCSetState {
- public:
-  // We use different types to represent the state value. Particularly SPARC puts
-  // values in structs from "left to right", i.e. MSB to LSB. This results in many
-  // unnecessary shift operations when loading and storing values of this type.
-  // This degrades performance significantly (>10%) on that platform.
-  // Other tested ABIs do not seem to have this problem, and actually tend to
-  // favor smaller types, so we use the smallest usable type there.
-#ifdef SPARC
-  #define CSETSTATE_FORMAT INTPTR_FORMAT
-  typedef intptr_t in_cset_state_t;
-#else
-  #define CSETSTATE_FORMAT "%d"
-  typedef int8_t in_cset_state_t;
-#endif
- private:
-  in_cset_state_t _value;
- public:
-  enum {
-    // Selection of the values were driven to micro-optimize the encoding and
-    // frequency of the checks.
-    // The most common check is whether the region is in the collection set or not,
-    // this encoding allows us to use an > 0 check.
-    // The positive values are encoded in increasing generation order, which
-    // makes getting the next generation fast by a simple increment. They are also
-    // used to index into arrays.
-    // The negative values are used for objects requiring various special cases,
-    // for example eager reclamation of humongous objects or optional regions.
-    Optional     = -2,    // The region is optional
-    Humongous    = -1,    // The region is humongous
-    NotInCSet    =  0,    // The region is not in the collection set.
-    Young        =  1,    // The region is in the collection set and a young region.
-    Old          =  2,    // The region is in the collection set and an old region.
-    Num
-  };
-
-  InCSetState(in_cset_state_t value = NotInCSet) : _value(value) {
-    assert(is_valid(), "Invalid state %d", _value);
-  }
-
-  in_cset_state_t value() const        { return _value; }
-
-  void set_old()                       { _value = Old; }
-
-  bool is_in_cset_or_humongous() const { return is_in_cset() || is_humongous(); }
-  bool is_in_cset() const              { return _value > NotInCSet; }
-
-  bool is_humongous() const            { return _value == Humongous; }
-  bool is_young() const                { return _value == Young; }
-  bool is_old() const                  { return _value == Old; }
-  bool is_optional() const             { return _value == Optional; }
-
-#ifdef ASSERT
-  bool is_default() const              { return _value == NotInCSet; }
-  bool is_valid() const                { return (_value >= Optional) && (_value < Num); }
-  bool is_valid_gen() const            { return (_value >= Young && _value <= Old); }
-#endif
-};
-
-// Instances of this class are used for quick tests on whether a reference points
-// into the collection set and into which generation or is a humongous object
-//
-// Each of the array's elements indicates whether the corresponding region is in
-// the collection set and if so in which generation, or a humongous region.
-//
-// We use this to speed up reference processing during young collection and
-// quickly reclaim humongous objects. For the latter, by making a humongous region
-// succeed this test, we sort-of add it to the collection set. During the reference
-// iteration closures, when we see a humongous region, we then simply mark it as
-// referenced, i.e. live.
-class G1InCSetStateFastTestBiasedMappedArray : public G1BiasedMappedArray<InCSetState> {
- protected:
-  InCSetState default_value() const { return InCSetState::NotInCSet; }
- public:
-  void set_optional(uintptr_t index) {
-    assert(get_by_index(index).is_default(),
-           "State at index " INTPTR_FORMAT " should be default but is " CSETSTATE_FORMAT, index, get_by_index(index).value());
-    set_by_index(index, InCSetState::Optional);
-  }
-
-  void set_humongous(uintptr_t index) {
-    assert(get_by_index(index).is_default(),
-           "State at index " INTPTR_FORMAT " should be default but is " CSETSTATE_FORMAT, index, get_by_index(index).value());
-    set_by_index(index, InCSetState::Humongous);
-  }
-
-  void clear_humongous(uintptr_t index) {
-    set_by_index(index, InCSetState::NotInCSet);
-  }
-
-  void set_in_young(uintptr_t index) {
-    assert(get_by_index(index).is_default(),
-           "State at index " INTPTR_FORMAT " should be default but is " CSETSTATE_FORMAT, index, get_by_index(index).value());
-    set_by_index(index, InCSetState::Young);
-  }
-
-  void set_in_old(uintptr_t index) {
-    assert(get_by_index(index).is_default(),
-           "State at index " INTPTR_FORMAT " should be default but is " CSETSTATE_FORMAT, index, get_by_index(index).value());
-    set_by_index(index, InCSetState::Old);
-  }
-
-  bool is_in_cset_or_humongous(HeapWord* addr) const { return at(addr).is_in_cset_or_humongous(); }
-  bool is_in_cset(HeapWord* addr) const { return at(addr).is_in_cset(); }
-  bool is_in_cset(const HeapRegion* hr) const { return get_by_index(hr->hrm_index()).is_in_cset(); }
-  InCSetState at(HeapWord* addr) const { return get_by_address(addr); }
-  void clear() { G1BiasedMappedArray<InCSetState>::clear(); }
-  void clear(const HeapRegion* hr) { return set_by_index(hr->hrm_index(), InCSetState::NotInCSet); }
-};
-
-#endif // SHARE_GC_G1_G1INCSETSTATE_HPP
--- a/src/hotspot/share/gc/g1/g1OopClosures.hpp	Tue May 14 08:52:51 2019 -0400
+++ b/src/hotspot/share/gc/g1/g1OopClosures.hpp	Tue May 14 15:36:26 2019 +0200
@@ -25,7 +25,7 @@
 #ifndef SHARE_GC_G1_G1OOPCLOSURES_HPP
 #define SHARE_GC_G1_G1OOPCLOSURES_HPP
 
-#include "gc/g1/g1InCSetState.hpp"
+#include "gc/g1/g1HeapRegionAttr.hpp"
 #include "memory/iterator.hpp"
 #include "oops/markOop.hpp"
 
@@ -52,18 +52,18 @@
   inline void prefetch_and_push(T* p, oop const obj);
 
   template <class T>
-  inline void handle_non_cset_obj_common(InCSetState const state, T* p, oop const obj);
+  inline void handle_non_cset_obj_common(G1HeapRegionAttr const region_attr, T* p, oop const obj);
 public:
   virtual ReferenceIterationMode reference_iteration_mode() { return DO_FIELDS; }
 
   inline void trim_queue_partially();
 };
 
-// Used during the Update RS phase to refine remaining cards in the DCQ during garbage collection.
-class G1ScanObjsDuringUpdateRSClosure : public G1ScanClosureBase {
+// Used to scan cards from the DCQS or the remembered sets during garbage collection.
+class G1ScanCardClosure : public G1ScanClosureBase {
 public:
-  G1ScanObjsDuringUpdateRSClosure(G1CollectedHeap* g1h,
-                                  G1ParScanThreadState* pss) :
+  G1ScanCardClosure(G1CollectedHeap* g1h,
+                    G1ParScanThreadState* pss) :
     G1ScanClosureBase(g1h, pss) { }
 
   template <class T> void do_oop_work(T* p);
@@ -71,23 +71,11 @@
   virtual void do_oop(oop* p)       { do_oop_work(p); }
 };
 
-// Used during the Scan RS phase to scan cards from the remembered set during garbage collection.
-class G1ScanObjsDuringScanRSClosure : public G1ScanClosureBase {
-public:
-  G1ScanObjsDuringScanRSClosure(G1CollectedHeap* g1h,
-                                G1ParScanThreadState* par_scan_state):
-    G1ScanClosureBase(g1h, par_scan_state) { }
-
-  template <class T> void do_oop_work(T* p);
-  virtual void do_oop(oop* p)          { do_oop_work(p); }
-  virtual void do_oop(narrowOop* p)    { do_oop_work(p); }
-};
-
 // Used during Optional RS scanning to make sure we trim the queues in a timely manner.
 class G1ScanRSForOptionalClosure : public OopClosure {
-  G1ScanObjsDuringScanRSClosure* _scan_cl;
+  G1ScanCardClosure* _scan_cl;
 public:
-  G1ScanRSForOptionalClosure(G1ScanObjsDuringScanRSClosure* cl) : _scan_cl(cl) { }
+  G1ScanRSForOptionalClosure(G1ScanCardClosure* cl) : _scan_cl(cl) { }
 
   template <class T> void do_oop_work(T* p);
   virtual void do_oop(oop* p)          { do_oop_work(p); }
--- a/src/hotspot/share/gc/g1/g1OopClosures.inline.hpp	Tue May 14 08:52:51 2019 -0400
+++ b/src/hotspot/share/gc/g1/g1OopClosures.inline.hpp	Tue May 14 15:36:26 2019 +0200
@@ -61,10 +61,10 @@
 }
 
 template <class T>
-inline void G1ScanClosureBase::handle_non_cset_obj_common(InCSetState const state, T* p, oop const obj) {
-  if (state.is_humongous()) {
+inline void G1ScanClosureBase::handle_non_cset_obj_common(G1HeapRegionAttr const region_attr, T* p, oop const obj) {
+  if (region_attr.is_humongous()) {
     _g1h->set_humongous_is_live(obj);
-  } else if (state.is_optional()) {
+  } else if (region_attr.is_optional()) {
     _par_scan_state->remember_reference_into_optional_region(p);
   }
 }
@@ -81,16 +81,16 @@
     return;
   }
   oop obj = CompressedOops::decode_not_null(heap_oop);
-  const InCSetState state = _g1h->in_cset_state(obj);
-  if (state.is_in_cset()) {
+  const G1HeapRegionAttr region_attr = _g1h->region_attr(obj);
+  if (region_attr.is_in_cset()) {
     prefetch_and_push(p, obj);
   } else if (!HeapRegion::is_in_same_region(p, obj)) {
-    handle_non_cset_obj_common(state, p, obj);
+    handle_non_cset_obj_common(region_attr, p, obj);
     assert(_scanning_in_young != Uninitialized, "Scan location has not been initialized.");
     if (_scanning_in_young == True) {
       return;
     }
-    _par_scan_state->enqueue_card_if_tracked(p, obj);
+    _par_scan_state->enqueue_card_if_tracked(region_attr, p, obj);
   }
 }
 
@@ -160,7 +160,7 @@
 }
 
 template <class T>
-inline void G1ScanObjsDuringUpdateRSClosure::do_oop_work(T* p) {
+inline void G1ScanCardClosure::do_oop_work(T* p) {
   T o = RawAccess<>::oop_load(p);
   if (CompressedOops::is_null(o)) {
     return;
@@ -169,31 +169,15 @@
 
   check_obj_during_refinement(p, obj);
 
-  assert(!_g1h->is_in_cset((HeapWord*)p), "Oop originates from " PTR_FORMAT " (region: %u) which is in the collection set.", p2i(p), _g1h->addr_to_region((HeapWord*)p));
-  const InCSetState state = _g1h->in_cset_state(obj);
-  if (state.is_in_cset()) {
-    // Since the source is always from outside the collection set, here we implicitly know
-    // that this is a cross-region reference too.
+  // We can not check for references from the collection set: the remembered sets
+  // may contain such entries and we do not filter them before.
+
+  const G1HeapRegionAttr region_attr = _g1h->region_attr(obj);
+  if (region_attr.is_in_cset()) {
     prefetch_and_push(p, obj);
   } else if (!HeapRegion::is_in_same_region(p, obj)) {
-    handle_non_cset_obj_common(state, p, obj);
-    _par_scan_state->enqueue_card_if_tracked(p, obj);
-  }
-}
-
-template <class T>
-inline void G1ScanObjsDuringScanRSClosure::do_oop_work(T* p) {
-  T heap_oop = RawAccess<>::oop_load(p);
-  if (CompressedOops::is_null(heap_oop)) {
-    return;
-  }
-  oop obj = CompressedOops::decode_not_null(heap_oop);
-
-  const InCSetState state = _g1h->in_cset_state(obj);
-  if (state.is_in_cset()) {
-    prefetch_and_push(p, obj);
-  } else if (!HeapRegion::is_in_same_region(p, obj)) {
-    handle_non_cset_obj_common(state, p, obj);
+    handle_non_cset_obj_common(region_attr, p, obj);
+    _par_scan_state->enqueue_card_if_tracked(region_attr, p, obj);
   }
 }
 
@@ -233,7 +217,7 @@
 
   assert(_worker_id == _par_scan_state->worker_id(), "sanity");
 
-  const InCSetState state = _g1h->in_cset_state(obj);
+  const G1HeapRegionAttr state = _g1h->region_attr(obj);
   if (state.is_in_cset()) {
     oop forwardee;
     markOop m = obj->mark_raw();
--- a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp	Tue May 14 08:52:51 2019 -0400
+++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp	Tue May 14 15:36:26 2019 +0200
@@ -75,11 +75,11 @@
 
   _plab_allocator = new G1PLABAllocator(_g1h->allocator());
 
-  _dest[InCSetState::NotInCSet]    = InCSetState::NotInCSet;
+  _dest[G1HeapRegionAttr::NotInCSet] = G1HeapRegionAttr::NotInCSet;
   // The dest for Young is used when the objects are aged enough to
   // need to be moved to the next space.
-  _dest[InCSetState::Young]        = InCSetState::Old;
-  _dest[InCSetState::Old]          = InCSetState::Old;
+  _dest[G1HeapRegionAttr::Young] = G1HeapRegionAttr::Old;
+  _dest[G1HeapRegionAttr::Old]   = G1HeapRegionAttr::Old;
 
   _closures = G1EvacuationRootClosures::create_root_closures(this, _g1h);
 
@@ -157,18 +157,18 @@
   } while (!_refs->is_empty());
 }
 
-HeapWord* G1ParScanThreadState::allocate_in_next_plab(InCSetState const state,
-                                                      InCSetState* dest,
+HeapWord* G1ParScanThreadState::allocate_in_next_plab(G1HeapRegionAttr const region_attr,
+                                                      G1HeapRegionAttr* dest,
                                                       size_t word_sz,
                                                       bool previous_plab_refill_failed) {
-  assert(state.is_in_cset_or_humongous(), "Unexpected state: " CSETSTATE_FORMAT, state.value());
-  assert(dest->is_in_cset_or_humongous(), "Unexpected dest: " CSETSTATE_FORMAT, dest->value());
+  assert(region_attr.is_in_cset_or_humongous(), "Unexpected region attr type: %s", region_attr.get_type_str());
+  assert(dest->is_in_cset_or_humongous(), "Unexpected dest: %s region attr", dest->get_type_str());
 
   // Right now we only have two types of regions (young / old) so
   // let's keep the logic here simple. We can generalize it when necessary.
   if (dest->is_young()) {
     bool plab_refill_in_old_failed = false;
-    HeapWord* const obj_ptr = _plab_allocator->allocate(InCSetState::Old,
+    HeapWord* const obj_ptr = _plab_allocator->allocate(G1HeapRegionAttr::Old,
                                                         word_sz,
                                                         &plab_refill_in_old_failed);
     // Make sure that we won't attempt to copy any other objects out
@@ -190,38 +190,38 @@
     return obj_ptr;
   } else {
     _old_gen_is_full = previous_plab_refill_failed;
-    assert(dest->is_old(), "Unexpected dest: " CSETSTATE_FORMAT, dest->value());
+    assert(dest->is_old(), "Unexpected dest region attr: %s", dest->get_type_str());
     // no other space to try.
     return NULL;
   }
 }
 
-InCSetState G1ParScanThreadState::next_state(InCSetState const state, markOop const m, uint& age) {
-  if (state.is_young()) {
+G1HeapRegionAttr G1ParScanThreadState::next_region_attr(G1HeapRegionAttr const region_attr, markOop const m, uint& age) {
+  if (region_attr.is_young()) {
     age = !m->has_displaced_mark_helper() ? m->age()
                                           : m->displaced_mark_helper()->age();
     if (age < _tenuring_threshold) {
-      return state;
+      return region_attr;
     }
   }
-  return dest(state);
+  return dest(region_attr);
 }
 
-void G1ParScanThreadState::report_promotion_event(InCSetState const dest_state,
+void G1ParScanThreadState::report_promotion_event(G1HeapRegionAttr const dest_attr,
                                                   oop const old, size_t word_sz, uint age,
                                                   HeapWord * const obj_ptr) const {
-  PLAB* alloc_buf = _plab_allocator->alloc_buffer(dest_state);
+  PLAB* alloc_buf = _plab_allocator->alloc_buffer(dest_attr);
   if (alloc_buf->contains(obj_ptr)) {
     _g1h->_gc_tracer_stw->report_promotion_in_new_plab_event(old->klass(), word_sz * HeapWordSize, age,
-                                                             dest_state.value() == InCSetState::Old,
+                                                             dest_attr.type() == G1HeapRegionAttr::Old,
                                                              alloc_buf->word_sz() * HeapWordSize);
   } else {
     _g1h->_gc_tracer_stw->report_promotion_outside_plab_event(old->klass(), word_sz * HeapWordSize, age,
-                                                              dest_state.value() == InCSetState::Old);
+                                                              dest_attr.type() == G1HeapRegionAttr::Old);
   }
 }
 
-oop G1ParScanThreadState::copy_to_survivor_space(InCSetState const state,
+oop G1ParScanThreadState::copy_to_survivor_space(G1HeapRegionAttr const region_attr,
                                                  oop const old,
                                                  markOop const old_mark) {
   const size_t word_sz = old->size();
@@ -232,21 +232,21 @@
          (!from_region->is_young() && young_index == 0), "invariant" );
 
   uint age = 0;
-  InCSetState dest_state = next_state(state, old_mark, age);
+  G1HeapRegionAttr dest_attr = next_region_attr(region_attr, old_mark, age);
   // The second clause is to prevent premature evacuation failure in case there
   // is still space in survivor, but old gen is full.
-  if (_old_gen_is_full && dest_state.is_old()) {
+  if (_old_gen_is_full && dest_attr.is_old()) {
     return handle_evacuation_failure_par(old, old_mark);
   }
-  HeapWord* obj_ptr = _plab_allocator->plab_allocate(dest_state, word_sz);
+  HeapWord* obj_ptr = _plab_allocator->plab_allocate(dest_attr, word_sz);
 
   // PLAB allocations should succeed most of the time, so we'll
   // normally check against NULL once and that's it.
   if (obj_ptr == NULL) {
     bool plab_refill_failed = false;
-    obj_ptr = _plab_allocator->allocate_direct_or_new_plab(dest_state, word_sz, &plab_refill_failed);
+    obj_ptr = _plab_allocator->allocate_direct_or_new_plab(dest_attr, word_sz, &plab_refill_failed);
     if (obj_ptr == NULL) {
-      obj_ptr = allocate_in_next_plab(state, &dest_state, word_sz, plab_refill_failed);
+      obj_ptr = allocate_in_next_plab(region_attr, &dest_attr, word_sz, plab_refill_failed);
       if (obj_ptr == NULL) {
         // This will either forward-to-self, or detect that someone else has
         // installed a forwarding pointer.
@@ -255,7 +255,7 @@
     }
     if (_g1h->_gc_tracer_stw->should_report_promotion_events()) {
       // The events are checked individually as part of the actual commit
-      report_promotion_event(dest_state, old, word_sz, age, obj_ptr);
+      report_promotion_event(dest_attr, old, word_sz, age, obj_ptr);
     }
   }
 
@@ -267,7 +267,7 @@
   if (_g1h->evacuation_should_fail()) {
     // Doing this after all the allocation attempts also tests the
     // undo_allocation() method too.
-    _plab_allocator->undo_allocation(dest_state, obj_ptr, word_sz);
+    _plab_allocator->undo_allocation(dest_attr, obj_ptr, word_sz);
     return handle_evacuation_failure_par(old, old_mark);
   }
 #endif // !PRODUCT
@@ -280,7 +280,7 @@
   if (forward_ptr == NULL) {
     Copy::aligned_disjoint_words((HeapWord*) old, obj_ptr, word_sz);
 
-    if (dest_state.is_young()) {
+    if (dest_attr.is_young()) {
       if (age < markOopDesc::max_age) {
         age++;
       }
@@ -300,8 +300,8 @@
     }
 
     if (G1StringDedup::is_enabled()) {
-      const bool is_from_young = state.is_young();
-      const bool is_to_young = dest_state.is_young();
+      const bool is_from_young = region_attr.is_young();
+      const bool is_to_young = dest_attr.is_young();
       assert(is_from_young == _g1h->heap_region_containing(old)->is_young(),
              "sanity");
       assert(is_to_young == _g1h->heap_region_containing(obj)->is_young(),
@@ -322,12 +322,12 @@
       oop* old_p = set_partial_array_mask(old);
       do_oop_partial_array(old_p);
     } else {
-      G1ScanInYoungSetter x(&_scanner, dest_state.is_young());
+      G1ScanInYoungSetter x(&_scanner, dest_attr.is_young());
       obj->oop_iterate_backwards(&_scanner);
     }
     return obj;
   } else {
-    _plab_allocator->undo_allocation(dest_state, obj_ptr, word_sz);
+    _plab_allocator->undo_allocation(dest_attr, obj_ptr, word_sz);
     return forward_ptr;
   }
 }
--- a/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp	Tue May 14 08:52:51 2019 -0400
+++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.hpp	Tue May 14 15:36:26 2019 +0200
@@ -45,17 +45,17 @@
 
 class G1ParScanThreadState : public CHeapObj<mtGC> {
   G1CollectedHeap* _g1h;
-  RefToScanQueue*  _refs;
+  RefToScanQueue* _refs;
   G1DirtyCardQueue _dcq;
-  G1CardTable*     _ct;
+  G1CardTable* _ct;
   G1EvacuationRootClosures* _closures;
 
-  G1PLABAllocator*  _plab_allocator;
+  G1PLABAllocator* _plab_allocator;
 
-  AgeTable          _age_table;
-  InCSetState       _dest[InCSetState::Num];
+  AgeTable _age_table;
+  G1HeapRegionAttr _dest[G1HeapRegionAttr::Num];
   // Local tenuring threshold.
-  uint              _tenuring_threshold;
+  uint _tenuring_threshold;
   G1ScanEvacuatedObjClosure  _scanner;
 
   uint _worker_id;
@@ -80,12 +80,12 @@
   G1DirtyCardQueue& dirty_card_queue()           { return _dcq; }
   G1CardTable* ct()                              { return _ct; }
 
-  InCSetState dest(InCSetState original) const {
+  G1HeapRegionAttr dest(G1HeapRegionAttr original) const {
     assert(original.is_valid(),
-           "Original state invalid: " CSETSTATE_FORMAT, original.value());
-    assert(_dest[original.value()].is_valid_gen(),
-           "Dest state is invalid: " CSETSTATE_FORMAT, _dest[original.value()].value());
-    return _dest[original.value()];
+           "Original region attr invalid: %s", original.get_type_str());
+    assert(_dest[original.type()].is_valid_gen(),
+           "Dest region attr is invalid: %s", _dest[original.type()].get_type_str());
+    return _dest[original.type()];
   }
 
   size_t _num_optional_regions;
@@ -111,10 +111,19 @@
   template <class T> void do_oop_ext(T* ref);
   template <class T> void push_on_queue(T* ref);
 
-  template <class T> void enqueue_card_if_tracked(T* p, oop o) {
+  template <class T> void enqueue_card_if_tracked(G1HeapRegionAttr region_attr, T* p, oop o) {
     assert(!HeapRegion::is_in_same_region(p, o), "Should have filtered out cross-region references already.");
     assert(!_g1h->heap_region_containing(p)->is_young(), "Should have filtered out from-young references already.");
-    if (!_g1h->heap_region_containing((HeapWord*)o)->rem_set()->is_tracked()) {
+
+#ifdef ASSERT
+    HeapRegion* const hr_obj = _g1h->heap_region_containing((HeapWord*)o);
+    assert(region_attr.needs_remset_update() == hr_obj->rem_set()->is_tracked(),
+           "State flag indicating remset tracking disagrees (%s) with actual remembered set (%s) for region %u",
+           BOOL_TO_STR(region_attr.needs_remset_update()),
+           BOOL_TO_STR(hr_obj->rem_set()->is_tracked()),
+           hr_obj->hrm_index());
+#endif
+    if (!region_attr.needs_remset_update()) {
       return;
     }
     size_t card_index = ct()->index_for(p);
@@ -184,14 +193,14 @@
   // Returns a non-NULL pointer if successful, and updates dest if required.
   // Also determines whether we should continue to try to allocate into the various
   // generations or just end trying to allocate.
-  HeapWord* allocate_in_next_plab(InCSetState const state,
-                                  InCSetState* dest,
+  HeapWord* allocate_in_next_plab(G1HeapRegionAttr const region_attr,
+                                  G1HeapRegionAttr* dest,
                                   size_t word_sz,
                                   bool previous_plab_refill_failed);
 
-  inline InCSetState next_state(InCSetState const state, markOop const m, uint& age);
+  inline G1HeapRegionAttr next_region_attr(G1HeapRegionAttr const region_attr, markOop const m, uint& age);
 
-  void report_promotion_event(InCSetState const dest_state,
+  void report_promotion_event(G1HeapRegionAttr const dest_attr,
                               oop const old, size_t word_sz, uint age,
                               HeapWord * const obj_ptr) const;
 
@@ -200,7 +209,7 @@
 
   inline void trim_queue_to_threshold(uint threshold);
 public:
-  oop copy_to_survivor_space(InCSetState const state, oop const obj, markOop const old_mark);
+  oop copy_to_survivor_space(G1HeapRegionAttr const region_attr, oop const obj, markOop const old_mark);
 
   void trim_queue();
   void trim_queue_partially();
--- a/src/hotspot/share/gc/g1/g1ParScanThreadState.inline.hpp	Tue May 14 08:52:51 2019 -0400
+++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.inline.hpp	Tue May 14 15:36:26 2019 +0200
@@ -41,14 +41,14 @@
   // than one thread might claim the same card. So the same card may be
   // processed multiple times, and so we might get references into old gen here.
   // So we need to redo this check.
-  const InCSetState in_cset_state = _g1h->in_cset_state(obj);
+  const G1HeapRegionAttr region_attr = _g1h->region_attr(obj);
   // References pushed onto the work stack should never point to a humongous region
   // as they are not added to the collection set due to above precondition.
-  assert(!in_cset_state.is_humongous(),
+  assert(!region_attr.is_humongous(),
          "Obj " PTR_FORMAT " should not refer to humongous region %u from " PTR_FORMAT,
          p2i(obj), _g1h->addr_to_region((HeapWord*)obj), p2i(p));
 
-  if (!in_cset_state.is_in_cset()) {
+  if (!region_attr.is_in_cset()) {
     // In this case somebody else already did all the work.
     return;
   }
@@ -57,7 +57,7 @@
   if (m->is_marked()) {
     obj = (oop) m->decode_pointer();
   } else {
-    obj = copy_to_survivor_space(in_cset_state, obj, m);
+    obj = copy_to_survivor_space(region_attr, obj, m);
   }
   RawAccess<IS_NOT_NULL>::oop_store(p, obj);
 
@@ -67,7 +67,7 @@
   }
   HeapRegion* from = _g1h->heap_region_containing(p);
   if (!from->is_young()) {
-    enqueue_card_if_tracked(p, obj);
+    enqueue_card_if_tracked(_g1h->region_attr(obj), p, obj);
   }
 }
 
--- a/src/hotspot/share/gc/g1/g1Policy.hpp	Tue May 14 08:52:51 2019 -0400
+++ b/src/hotspot/share/gc/g1/g1Policy.hpp	Tue May 14 15:36:26 2019 +0200
@@ -27,7 +27,7 @@
 
 #include "gc/g1/g1CollectorState.hpp"
 #include "gc/g1/g1GCPhaseTimes.hpp"
-#include "gc/g1/g1InCSetState.hpp"
+#include "gc/g1/g1HeapRegionAttr.hpp"
 #include "gc/g1/g1InitialMarkToMixedTimeTracker.hpp"
 #include "gc/g1/g1MMUTracker.hpp"
 #include "gc/g1/g1RemSetTrackingPolicy.hpp"
--- a/src/hotspot/share/gc/g1/g1RemSet.cpp	Tue May 14 08:52:51 2019 -0400
+++ b/src/hotspot/share/gc/g1/g1RemSet.cpp	Tue May 14 15:36:26 2019 +0200
@@ -305,7 +305,7 @@
 }
 
 G1ScanRSForRegionClosure::G1ScanRSForRegionClosure(G1RemSetScanState* scan_state,
-                                                   G1ScanObjsDuringScanRSClosure* scan_obj_on_card,
+                                                   G1ScanCardClosure* scan_obj_on_card,
                                                    G1ParScanThreadState* pss,
                                                    G1GCPhaseTimes::GCParPhases phase,
                                                    uint worker_i) :
@@ -345,7 +345,7 @@
 
   G1OopStarChunkedList* opt_rem_set_list = _pss->oops_into_optional_region(r);
 
-  G1ScanObjsDuringScanRSClosure scan_cl(_g1h, _pss);
+  G1ScanCardClosure scan_cl(_g1h, _pss);
   G1ScanRSForOptionalClosure cl(&scan_cl);
   _opt_refs_scanned += opt_rem_set_list->oops_do(&cl, _pss->closures()->raw_strong_oops());
   _opt_refs_memory_used += opt_rem_set_list->used_memory();
@@ -464,7 +464,7 @@
                             G1GCPhaseTimes::GCParPhases coderoots_phase) {
   assert(pss->trim_ticks().value() == 0, "Queues must have been trimmed before entering.");
 
-  G1ScanObjsDuringScanRSClosure scan_cl(_g1h, pss);
+  G1ScanCardClosure scan_cl(_g1h, pss);
   G1ScanRSForRegionClosure cl(_scan_state, &scan_cl, pss, scan_phase, worker_i);
   _g1h->collection_set_iterate_increment_from(&cl, worker_i);
 
@@ -489,12 +489,12 @@
 // Closure used for updating rem sets. Only called during an evacuation pause.
 class G1RefineCardClosure: public G1CardTableEntryClosure {
   G1RemSet* _g1rs;
-  G1ScanObjsDuringUpdateRSClosure* _update_rs_cl;
+  G1ScanCardClosure* _update_rs_cl;
 
   size_t _cards_scanned;
   size_t _cards_skipped;
 public:
-  G1RefineCardClosure(G1CollectedHeap* g1h, G1ScanObjsDuringUpdateRSClosure* update_rs_cl) :
+  G1RefineCardClosure(G1CollectedHeap* g1h, G1ScanCardClosure* update_rs_cl) :
     _g1rs(g1h->rem_set()), _update_rs_cl(update_rs_cl), _cards_scanned(0), _cards_skipped(0)
   {}
 
@@ -527,7 +527,7 @@
   if (G1HotCardCache::default_use_cache()) {
     G1EvacPhaseTimesTracker x(p, pss, G1GCPhaseTimes::ScanHCC, worker_i);
 
-    G1ScanObjsDuringUpdateRSClosure scan_hcc_cl(_g1h, pss);
+    G1ScanCardClosure scan_hcc_cl(_g1h, pss);
     G1RefineCardClosure refine_card_cl(_g1h, &scan_hcc_cl);
     _g1h->iterate_hcc_closure(&refine_card_cl, worker_i);
   }
@@ -536,7 +536,7 @@
   {
     G1EvacPhaseTimesTracker x(p, pss, G1GCPhaseTimes::UpdateRS, worker_i);
 
-    G1ScanObjsDuringUpdateRSClosure update_rs_cl(_g1h, pss);
+    G1ScanCardClosure update_rs_cl(_g1h, pss);
     G1RefineCardClosure refine_card_cl(_g1h, &update_rs_cl);
     _g1h->iterate_dirty_card_closure(&refine_card_cl, worker_i);
 
@@ -712,7 +712,7 @@
 }
 
 bool G1RemSet::refine_card_during_gc(CardValue* card_ptr,
-                                     G1ScanObjsDuringUpdateRSClosure* update_rs_cl) {
+                                     G1ScanCardClosure* update_rs_cl) {
   assert(_g1h->is_gc_active(), "Only call during GC");
 
   // Construct the region representing the card.
--- a/src/hotspot/share/gc/g1/g1RemSet.hpp	Tue May 14 08:52:51 2019 -0400
+++ b/src/hotspot/share/gc/g1/g1RemSet.hpp	Tue May 14 15:36:26 2019 +0200
@@ -47,8 +47,7 @@
 class G1RemSetScanState;
 class G1ParScanThreadState;
 class G1Policy;
-class G1ScanObjsDuringScanRSClosure;
-class G1ScanObjsDuringUpdateRSClosure;
+class G1ScanCardClosure;
 class HeapRegionClaimer;
 
 // A G1RemSet in which each heap region has a rem set that records the
@@ -115,7 +114,7 @@
   // Refine the card corresponding to "card_ptr", applying the given closure to
   // all references found. Must only be called during gc.
   // Returns whether the card has been scanned.
-  bool refine_card_during_gc(CardValue* card_ptr, G1ScanObjsDuringUpdateRSClosure* update_rs_cl);
+  bool refine_card_during_gc(CardValue* card_ptr, G1ScanCardClosure* update_rs_cl);
 
   // Print accumulated summary info from the start of the VM.
   void print_summary_info();
@@ -135,7 +134,7 @@
   G1CardTable *_ct;
 
   G1ParScanThreadState* _pss;
-  G1ScanObjsDuringScanRSClosure* _scan_objs_on_card_cl;
+  G1ScanCardClosure* _scan_objs_on_card_cl;
 
   G1RemSetScanState* _scan_state;
 
@@ -164,7 +163,7 @@
   void scan_strong_code_roots(HeapRegion* r);
 public:
   G1ScanRSForRegionClosure(G1RemSetScanState* scan_state,
-                           G1ScanObjsDuringScanRSClosure* scan_obj_on_card,
+                           G1ScanCardClosure* scan_obj_on_card,
                            G1ParScanThreadState* pss,
                            G1GCPhaseTimes::GCParPhases phase,
                            uint worker_i);
--- a/test/hotspot/jtreg/gc/g1/TestEagerReclaimHumongousRegionsLog.java	Tue May 14 08:52:51 2019 -0400
+++ b/test/hotspot/jtreg/gc/g1/TestEagerReclaimHumongousRegionsLog.java	Tue May 14 15:36:26 2019 +0200
@@ -71,7 +71,7 @@
 
         // This gives an array of lines containing eager reclaim of humongous regions
         // log messages contents after the ":" in the following order for every GC:
-        //   Humongous Register: a.ams
+        //   Region Register: a.ams
         //   Humongous Total: b
         //   Humongous Candidate: c
         //   Humongous Reclaim: d.dms
@@ -79,7 +79,7 @@
         //   Humongous Regions: f->g
 
         String[] lines = Arrays.stream(output.getStdout().split("\\R"))
-                         .filter(s -> s.contains("Humongous")).map(s -> s.substring(s.indexOf(LogSeparator) + LogSeparator.length()))
+                         .filter(s -> (s.contains("Humongous") || s.contains("Region Register"))).map(s -> s.substring(s.indexOf(LogSeparator) + LogSeparator.length()))
                          .toArray(String[]::new);
 
         Asserts.assertTrue(lines.length % 6 == 0, "There seems to be an unexpected amount of log messages (total: " + lines.length + ") per GC");
--- a/test/hotspot/jtreg/gc/g1/TestGCLogMessages.java	Tue May 14 08:52:51 2019 -0400
+++ b/test/hotspot/jtreg/gc/g1/TestGCLogMessages.java	Tue May 14 15:36:26 2019 +0200
@@ -132,6 +132,7 @@
         new LogMessageWithLevel("Queue Fixup", Level.DEBUG),
         new LogMessageWithLevel("Table Fixup", Level.DEBUG),
         new LogMessageWithLevel("Expand Heap After Collection", Level.DEBUG),
+        new LogMessageWithLevel("Region Register", Level.DEBUG),
         // Free CSet
         new LogMessageWithLevel("Free Collection Set", Level.DEBUG),
         new LogMessageWithLevel("Free Collection Set Serial", Level.TRACE),
@@ -139,7 +140,6 @@
         new LogMessageWithLevel("Non-Young Free Collection Set", Level.TRACE),
         // Humongous Eager Reclaim
         new LogMessageWithLevel("Humongous Reclaim", Level.DEBUG),
-        new LogMessageWithLevel("Humongous Register", Level.DEBUG),
         // Merge PSS
         new LogMessageWithLevel("Merge Per-Thread State", Level.DEBUG),
         // TLAB handling