changeset 54465:c4f16445675a

8218668: Clean up evacuation of optional collection set Summary: Better integrate optional collection set evacuation into the existing evacuation scheme, fixing a few minor issues with the initial implementation. Reviewed-by: kbarrett, sangheki
author tschatzl
date Mon, 08 Apr 2019 20:37:52 +0200
parents 1f8938ce8564
children 58751415d5f8
files src/hotspot/share/gc/g1/g1CollectedHeap.cpp src/hotspot/share/gc/g1/g1CollectedHeap.hpp src/hotspot/share/gc/g1/g1CollectionSet.cpp src/hotspot/share/gc/g1/g1CollectionSet.hpp src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp src/hotspot/share/gc/g1/g1CollectionSetCandidates.hpp src/hotspot/share/gc/g1/g1ConcurrentMark.cpp src/hotspot/share/gc/g1/g1EvacFailure.cpp src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp src/hotspot/share/gc/g1/g1GCPhaseTimes.hpp src/hotspot/share/gc/g1/g1HeapVerifier.cpp src/hotspot/share/gc/g1/g1OopStarChunkedList.cpp src/hotspot/share/gc/g1/g1OopStarChunkedList.hpp src/hotspot/share/gc/g1/g1OopStarChunkedList.inline.hpp src/hotspot/share/gc/g1/g1ParScanThreadState.cpp src/hotspot/share/gc/g1/g1Policy.cpp src/hotspot/share/gc/g1/g1Policy.hpp src/hotspot/share/gc/g1/g1RemSet.cpp src/hotspot/share/gc/g1/g1RemSet.hpp src/hotspot/share/gc/g1/heapRegion.cpp src/hotspot/share/gc/g1/heapRegion.hpp src/hotspot/share/gc/shared/workerDataArray.hpp src/hotspot/share/gc/shared/workerDataArray.inline.hpp
diffstat 23 files changed, 798 insertions(+), 720 deletions(-) [+]
line wrap: on
line diff
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.cpp	Mon Apr 08 11:11:22 2019 -0700
+++ b/src/hotspot/share/gc/g1/g1CollectedHeap.cpp	Mon Apr 08 20:37:52 2019 +0200
@@ -154,6 +154,11 @@
   reset_from_card_cache(start_idx, num_regions);
 }
 
+Tickspan G1CollectedHeap::run_task(AbstractGangTask* task) {
+  Ticks start = Ticks::now();
+  workers()->run_task(task, workers()->active_workers());
+  return Ticks::now() - start;
+}
 
 HeapRegion* G1CollectedHeap::new_heap_region(uint hrs_index,
                                              MemRegion mr) {
@@ -2242,12 +2247,12 @@
   _hrm->par_iterate(cl, hrclaimer, 0);
 }
 
-void G1CollectedHeap::collection_set_iterate(HeapRegionClosure* cl) {
+void G1CollectedHeap::collection_set_iterate_all(HeapRegionClosure* cl) {
   _collection_set.iterate(cl);
 }
 
-void G1CollectedHeap::collection_set_iterate_from(HeapRegionClosure *cl, uint worker_id) {
-  _collection_set.iterate_from(cl, worker_id, workers()->active_workers());
+void G1CollectedHeap::collection_set_iterate_increment_from(HeapRegionClosure *cl, uint worker_id) {
+  _collection_set.iterate_incremental_part_from(cl, worker_id, workers()->active_workers());
 }
 
 HeapWord* G1CollectedHeap::block_start(const void* addr) const {
@@ -2484,7 +2489,7 @@
 
 void G1CollectedHeap::print_cset_rsets() {
   PrintRSetsClosure cl("Printing CSet RSets");
-  collection_set_iterate(&cl);
+  collection_set_iterate_all(&cl);
 }
 
 void G1CollectedHeap::print_all_rsets() {
@@ -2880,15 +2885,18 @@
   phase_times()->record_start_new_cset_time_ms((os::elapsedTime() - start) * 1000.0);
 }
 
-void G1CollectedHeap::calculate_collection_set(G1EvacuationInfo& evacuation_info, double target_pause_time_ms){
-  policy()->finalize_collection_set(target_pause_time_ms, &_survivor);
-  evacuation_info.set_collectionset_regions(collection_set()->region_length());
+void G1CollectedHeap::calculate_collection_set(G1EvacuationInfo& evacuation_info, double target_pause_time_ms) {
+
+  _collection_set.finalize_initial_collection_set(target_pause_time_ms, &_survivor);
+  evacuation_info.set_collectionset_regions(collection_set()->region_length() +
+                                            collection_set()->optional_region_length());
 
   _cm->verify_no_collection_set_oops();
 
   if (_hr_printer.is_active()) {
     G1PrintCollectionSetClosure cl(&_hr_printer);
     _collection_set.iterate(&cl);
+    _collection_set.iterate_optional(&cl);
   }
 }
 
@@ -3060,9 +3068,10 @@
         pre_evacuate_collection_set(evacuation_info);
 
         // Actually do the work...
-        evacuate_collection_set(&per_thread_states);
-        evacuate_optional_collection_set(&per_thread_states);
-
+        evacuate_initial_collection_set(&per_thread_states);
+        if (_collection_set.optional_region_length() != 0) {
+          evacuate_optional_collection_set(&per_thread_states);
+        }
         post_evacuate_collection_set(evacuation_info, &per_thread_states);
 
         start_new_collection_set();
@@ -3088,7 +3097,8 @@
 
         double sample_end_time_sec = os::elapsedTime();
         double pause_time_ms = (sample_end_time_sec - sample_start_time_sec) * MILLIUNITS;
-        size_t total_cards_scanned = phase_times()->sum_thread_work_items(G1GCPhaseTimes::ScanRS, G1GCPhaseTimes::ScanRSScannedCards);
+        size_t total_cards_scanned = phase_times()->sum_thread_work_items(G1GCPhaseTimes::ScanRS, G1GCPhaseTimes::ScanRSScannedCards) +
+                                     phase_times()->sum_thread_work_items(G1GCPhaseTimes::OptScanRS, G1GCPhaseTimes::ScanRSScannedCards);
         policy()->record_collection_pause_end(pause_time_ms, total_cards_scanned, heap_used_bytes_before_gc);
       }
 
@@ -3192,86 +3202,6 @@
   } while (!offer_termination());
 }
 
-class G1ParTask : public AbstractGangTask {
-protected:
-  G1CollectedHeap*         _g1h;
-  G1ParScanThreadStateSet* _pss;
-  RefToScanQueueSet*       _queues;
-  G1RootProcessor*         _root_processor;
-  TaskTerminator           _terminator;
-  uint                     _n_workers;
-
-public:
-  G1ParTask(G1CollectedHeap* g1h, G1ParScanThreadStateSet* per_thread_states, RefToScanQueueSet *task_queues, G1RootProcessor* root_processor, uint n_workers)
-    : AbstractGangTask("G1 collection"),
-      _g1h(g1h),
-      _pss(per_thread_states),
-      _queues(task_queues),
-      _root_processor(root_processor),
-      _terminator(n_workers, _queues),
-      _n_workers(n_workers)
-  {}
-
-  void work(uint worker_id) {
-    if (worker_id >= _n_workers) return;  // no work needed this round
-
-    double start_sec = os::elapsedTime();
-    _g1h->phase_times()->record_time_secs(G1GCPhaseTimes::GCWorkerStart, worker_id, start_sec);
-
-    {
-      ResourceMark rm;
-      HandleMark   hm;
-
-      ReferenceProcessor*             rp = _g1h->ref_processor_stw();
-
-      G1ParScanThreadState*           pss = _pss->state_for_worker(worker_id);
-      pss->set_ref_discoverer(rp);
-
-      double start_strong_roots_sec = os::elapsedTime();
-
-      _root_processor->evacuate_roots(pss, worker_id);
-
-      _g1h->rem_set()->oops_into_collection_set_do(pss, worker_id);
-
-      double strong_roots_sec = os::elapsedTime() - start_strong_roots_sec;
-
-      double term_sec = 0.0;
-      size_t evac_term_attempts = 0;
-      {
-        double start = os::elapsedTime();
-        G1ParEvacuateFollowersClosure evac(_g1h, pss, _queues, _terminator.terminator(), G1GCPhaseTimes::ObjCopy);
-        evac.do_void();
-
-        evac_term_attempts = evac.term_attempts();
-        term_sec = evac.term_time();
-        double elapsed_sec = os::elapsedTime() - start;
-
-        G1GCPhaseTimes* p = _g1h->phase_times();
-        p->add_time_secs(G1GCPhaseTimes::ObjCopy, worker_id, elapsed_sec - term_sec);
-
-        p->record_or_add_thread_work_item(G1GCPhaseTimes::ObjCopy,
-                                          worker_id,
-                                          pss->lab_waste_words() * HeapWordSize,
-                                          G1GCPhaseTimes::ObjCopyLABWaste);
-        p->record_or_add_thread_work_item(G1GCPhaseTimes::ObjCopy,
-                                          worker_id,
-                                          pss->lab_undo_waste_words() * HeapWordSize,
-                                          G1GCPhaseTimes::ObjCopyLABUndoWaste);
-
-        p->record_time_secs(G1GCPhaseTimes::Termination, worker_id, term_sec);
-        p->record_thread_work_item(G1GCPhaseTimes::Termination, worker_id, evac_term_attempts);
-      }
-
-      assert(pss->queue_is_empty(), "should be empty");
-
-      // Close the inner scope so that the ResourceMark and HandleMark
-      // destructors are executed here and are included as part of the
-      // "GC Worker Time".
-    }
-    _g1h->phase_times()->record_time_secs(G1GCPhaseTimes::GCWorkerEnd, worker_id, os::elapsedTime());
-  }
-};
-
 void G1CollectedHeap::complete_cleaning(BoolObjectClosure* is_alive,
                                         bool class_unloading_occurred) {
   uint num_workers = workers()->active_workers();
@@ -3675,176 +3605,196 @@
     double recorded_clear_claimed_marks_time_ms = (os::elapsedTime() - start_clear_claimed_marks) * 1000.0;
     phase_times()->record_clear_claimed_marks_time_ms(recorded_clear_claimed_marks_time_ms);
   }
-}
-
-void G1CollectedHeap::evacuate_collection_set(G1ParScanThreadStateSet* per_thread_states) {
+
   // Should G1EvacuationFailureALot be in effect for this GC?
   NOT_PRODUCT(set_evacuation_failure_alot_for_current_gc();)
 
   assert(dirty_card_queue_set().completed_buffers_num() == 0, "Should be empty");
-
-  double start_par_time_sec = os::elapsedTime();
-  double end_par_time_sec;
-
-  {
-    const uint n_workers = workers()->active_workers();
-    G1RootProcessor root_processor(this, n_workers);
-    G1ParTask g1_par_task(this, per_thread_states, _task_queues, &root_processor, n_workers);
-
-    workers()->run_task(&g1_par_task);
-    end_par_time_sec = os::elapsedTime();
-
-    // Closing the inner scope will execute the destructor
-    // for the G1RootProcessor object. We record the current
-    // elapsed time before closing the scope so that time
-    // taken for the destructor is NOT included in the
-    // reported parallel time.
-  }
-
-  double par_time_ms = (end_par_time_sec - start_par_time_sec) * 1000.0;
-  phase_times()->record_par_time(par_time_ms);
-
-  double code_root_fixup_time_ms =
-        (os::elapsedTime() - end_par_time_sec) * 1000.0;
-  phase_times()->record_code_root_fixup_time(code_root_fixup_time_ms);
 }
 
-class G1EvacuateOptionalRegionTask : public AbstractGangTask {
+class G1EvacuateRegionsBaseTask : public AbstractGangTask {
+protected:
   G1CollectedHeap* _g1h;
   G1ParScanThreadStateSet* _per_thread_states;
-  G1OptionalCSet* _optional;
-  RefToScanQueueSet* _queues;
-  ParallelTaskTerminator _terminator;
-
-  Tickspan trim_ticks(G1ParScanThreadState* pss) {
-    Tickspan copy_time = pss->trim_ticks();
-    pss->reset_trim_ticks();
-    return copy_time;
+  RefToScanQueueSet* _task_queues;
+  TaskTerminator _terminator;
+  uint _num_workers;
+
+  void evacuate_live_objects(G1ParScanThreadState* pss,
+                             uint worker_id,
+                             G1GCPhaseTimes::GCParPhases objcopy_phase,
+                             G1GCPhaseTimes::GCParPhases termination_phase) {
+    G1GCPhaseTimes* p = _g1h->phase_times();
+
+    Ticks start = Ticks::now();
+    G1ParEvacuateFollowersClosure cl(_g1h, pss, _task_queues, _terminator.terminator(), objcopy_phase);
+    cl.do_void();
+
+    assert(pss->queue_is_empty(), "should be empty");
+
+    Tickspan evac_time = (Ticks::now() - start);
+    p->record_or_add_time_secs(objcopy_phase, worker_id, evac_time.seconds() - cl.term_time());
+
+    p->record_or_add_thread_work_item(objcopy_phase, worker_id, pss->lab_waste_words() * HeapWordSize, G1GCPhaseTimes::ObjCopyLABWaste);
+    p->record_or_add_thread_work_item(objcopy_phase, worker_id, pss->lab_undo_waste_words() * HeapWordSize, G1GCPhaseTimes::ObjCopyLABUndoWaste);
+
+    if (termination_phase == G1GCPhaseTimes::Termination) {
+      p->record_time_secs(termination_phase, worker_id, cl.term_time());
+      p->record_thread_work_item(termination_phase, worker_id, cl.term_attempts());
+    } else {
+      p->record_or_add_time_secs(termination_phase, worker_id, cl.term_time());
+      p->record_or_add_thread_work_item(termination_phase, worker_id, cl.term_attempts());
+    }
+    assert(pss->trim_ticks().seconds() == 0.0, "Unexpected partial trimming during evacuation");
   }
 
-  void scan_roots(G1ParScanThreadState* pss, uint worker_id) {
-    G1EvacuationRootClosures* root_cls = pss->closures();
-    G1ScanObjsDuringScanRSClosure obj_cl(_g1h, pss);
-
-    size_t scanned = 0;
-    size_t claimed = 0;
-    size_t skipped = 0;
-    size_t used_memory = 0;
-
-    Ticks    start = Ticks::now();
-    Tickspan copy_time;
-
-    for (uint i = _optional->current_index(); i < _optional->current_limit(); i++) {
-      HeapRegion* hr = _optional->region_at(i);
-      G1ScanRSForOptionalClosure scan_opt_cl(&obj_cl);
-      pss->oops_into_optional_region(hr)->oops_do(&scan_opt_cl, root_cls->raw_strong_oops());
-      copy_time += trim_ticks(pss);
-
-      G1ScanRSForRegionClosure scan_rs_cl(_g1h->rem_set()->scan_state(), &obj_cl, pss, G1GCPhaseTimes::OptScanRS, worker_id);
-      scan_rs_cl.do_heap_region(hr);
-      copy_time += trim_ticks(pss);
-      scanned += scan_rs_cl.cards_scanned();
-      claimed += scan_rs_cl.cards_claimed();
-      skipped += scan_rs_cl.cards_skipped();
-
-      // Chunk lists for this region is no longer needed.
-      used_memory += pss->oops_into_optional_region(hr)->used_memory();
+  virtual void start_work(uint worker_id) { }
+
+  virtual void end_work(uint worker_id) { }
+
+  virtual void scan_roots(G1ParScanThreadState* pss, uint worker_id) = 0;
+
+  virtual void evacuate_live_objects(G1ParScanThreadState* pss, uint worker_id) = 0;
+
+public:
+  G1EvacuateRegionsBaseTask(const char* name, G1ParScanThreadStateSet* per_thread_states, RefToScanQueueSet* task_queues, uint num_workers) :
+    AbstractGangTask(name),
+    _g1h(G1CollectedHeap::heap()),
+    _per_thread_states(per_thread_states),
+    _task_queues(task_queues),
+    _terminator(num_workers, _task_queues),
+    _num_workers(num_workers)
+  { }
+
+  void work(uint worker_id) {
+    start_work(worker_id);
+
+    {
+      ResourceMark rm;
+      HandleMark   hm;
+
+      G1ParScanThreadState* pss = _per_thread_states->state_for_worker(worker_id);
+      pss->set_ref_discoverer(_g1h->ref_processor_stw());
+
+      scan_roots(pss, worker_id);
+      evacuate_live_objects(pss, worker_id);
     }
 
-    Tickspan scan_time = (Ticks::now() - start) - copy_time;
-    G1GCPhaseTimes* p = _g1h->phase_times();
-    p->record_or_add_time_secs(G1GCPhaseTimes::OptScanRS, worker_id, scan_time.seconds());
-    p->record_or_add_time_secs(G1GCPhaseTimes::OptObjCopy, worker_id, copy_time.seconds());
-
-    p->record_or_add_thread_work_item(G1GCPhaseTimes::OptScanRS, worker_id, scanned, G1GCPhaseTimes::OptCSetScannedCards);
-    p->record_or_add_thread_work_item(G1GCPhaseTimes::OptScanRS, worker_id, claimed, G1GCPhaseTimes::OptCSetClaimedCards);
-    p->record_or_add_thread_work_item(G1GCPhaseTimes::OptScanRS, worker_id, skipped, G1GCPhaseTimes::OptCSetSkippedCards);
-    p->record_or_add_thread_work_item(G1GCPhaseTimes::OptScanRS, worker_id, used_memory, G1GCPhaseTimes::OptCSetUsedMemory);
-  }
-
-  void evacuate_live_objects(G1ParScanThreadState* pss, uint worker_id) {
-    Ticks start = Ticks::now();
-    G1ParEvacuateFollowersClosure cl(_g1h, pss, _queues, &_terminator, G1GCPhaseTimes::OptObjCopy);
-    cl.do_void();
-
-    Tickspan evac_time = (Ticks::now() - start);
-    G1GCPhaseTimes* p = _g1h->phase_times();
-    p->record_or_add_time_secs(G1GCPhaseTimes::OptObjCopy, worker_id, evac_time.seconds());
-    assert(pss->trim_ticks().seconds() == 0.0, "Unexpected partial trimming done during optional evacuation");
-  }
-
- public:
-  G1EvacuateOptionalRegionTask(G1CollectedHeap* g1h,
-                               G1ParScanThreadStateSet* per_thread_states,
-                               G1OptionalCSet* cset,
-                               RefToScanQueueSet* queues,
-                               uint n_workers) :
-    AbstractGangTask("G1 Evacuation Optional Region Task"),
-    _g1h(g1h),
-    _per_thread_states(per_thread_states),
-    _optional(cset),
-    _queues(queues),
-    _terminator(n_workers, _queues) {
-  }
-
-  void work(uint worker_id) {
-    ResourceMark rm;
-    HandleMark  hm;
-
-    G1ParScanThreadState* pss = _per_thread_states->state_for_worker(worker_id);
-    pss->set_ref_discoverer(_g1h->ref_processor_stw());
-
-    scan_roots(pss, worker_id);
-    evacuate_live_objects(pss, worker_id);
+    end_work(worker_id);
   }
 };
 
-void G1CollectedHeap::evacuate_optional_regions(G1ParScanThreadStateSet* per_thread_states, G1OptionalCSet* ocset) {
-  class G1MarkScope : public MarkScope {};
-  G1MarkScope code_mark_scope;
-
-  G1EvacuateOptionalRegionTask task(this, per_thread_states, ocset, _task_queues, workers()->active_workers());
-  workers()->run_task(&task);
+class G1EvacuateRegionsTask : public G1EvacuateRegionsBaseTask {
+  G1RootProcessor* _root_processor;
+
+  void scan_roots(G1ParScanThreadState* pss, uint worker_id) {
+    _root_processor->evacuate_roots(pss, worker_id);
+    _g1h->rem_set()->update_rem_set(pss, worker_id);
+    _g1h->rem_set()->scan_rem_set(pss, worker_id, G1GCPhaseTimes::ScanRS, G1GCPhaseTimes::ObjCopy, G1GCPhaseTimes::CodeRoots);
+  }
+
+  void evacuate_live_objects(G1ParScanThreadState* pss, uint worker_id) {
+    G1EvacuateRegionsBaseTask::evacuate_live_objects(pss, worker_id, G1GCPhaseTimes::ObjCopy, G1GCPhaseTimes::Termination);
+  }
+
+  void start_work(uint worker_id) {
+    _g1h->phase_times()->record_time_secs(G1GCPhaseTimes::GCWorkerStart, worker_id, Ticks::now().seconds());
+  }
+
+  void end_work(uint worker_id) {
+    _g1h->phase_times()->record_time_secs(G1GCPhaseTimes::GCWorkerEnd, worker_id, Ticks::now().seconds());
+  }
+
+public:
+  G1EvacuateRegionsTask(G1CollectedHeap* g1h,
+                        G1ParScanThreadStateSet* per_thread_states,
+                        RefToScanQueueSet* task_queues,
+                        G1RootProcessor* root_processor,
+                        uint num_workers) :
+    G1EvacuateRegionsBaseTask("G1 Evacuate Regions", per_thread_states, task_queues, num_workers),
+    _root_processor(root_processor)
+  { }
+};
+
+void G1CollectedHeap::evacuate_initial_collection_set(G1ParScanThreadStateSet* per_thread_states) {
+  Tickspan task_time;
+  const uint num_workers = workers()->active_workers();
+
+  Ticks start_processing = Ticks::now();
+  {
+    G1RootProcessor root_processor(this, num_workers);
+    G1EvacuateRegionsTask g1_par_task(this, per_thread_states, _task_queues, &root_processor, num_workers);
+    task_time = run_task(&g1_par_task);
+    // Closing the inner scope will execute the destructor for the G1RootProcessor object.
+    // To extract its code root fixup time we measure total time of this scope and
+    // subtract from the time the WorkGang task took.
+  }
+  Tickspan total_processing = Ticks::now() - start_processing;
+
+  G1GCPhaseTimes* p = phase_times();
+  p->record_initial_evac_time(task_time.seconds() * 1000.0);
+  p->record_or_add_code_root_fixup_time((total_processing - task_time).seconds() * 1000.0);
 }
 
+class G1EvacuateOptionalRegionsTask : public G1EvacuateRegionsBaseTask {
+
+  void scan_roots(G1ParScanThreadState* pss, uint worker_id) {
+    _g1h->rem_set()->scan_rem_set(pss, worker_id, G1GCPhaseTimes::OptScanRS, G1GCPhaseTimes::OptObjCopy, G1GCPhaseTimes::OptCodeRoots);
+  }
+
+  void evacuate_live_objects(G1ParScanThreadState* pss, uint worker_id) {
+    G1EvacuateRegionsBaseTask::evacuate_live_objects(pss, worker_id, G1GCPhaseTimes::OptObjCopy, G1GCPhaseTimes::OptTermination);
+  }
+
+public:
+  G1EvacuateOptionalRegionsTask(G1ParScanThreadStateSet* per_thread_states,
+                                RefToScanQueueSet* queues,
+                                uint num_workers) :
+    G1EvacuateRegionsBaseTask("G1 Evacuate Optional Regions", per_thread_states, queues, num_workers) {
+  }
+};
+
+void G1CollectedHeap::evacuate_next_optional_regions(G1ParScanThreadStateSet* per_thread_states) {
+  class G1MarkScope : public MarkScope { };
+
+  Tickspan task_time;
+
+  Ticks start_processing = Ticks::now();
+  {
+    G1MarkScope code_mark_scope;
+    G1EvacuateOptionalRegionsTask task(per_thread_states, _task_queues, workers()->active_workers());
+    task_time = run_task(&task);
+    // See comment in evacuate_collection_set() for the reason of the scope.
+  }
+  Tickspan total_processing = Ticks::now() - start_processing;
+
+  G1GCPhaseTimes* p = phase_times();
+  p->record_or_add_code_root_fixup_time((total_processing - task_time).seconds() * 1000.0);
+}
+
 void G1CollectedHeap::evacuate_optional_collection_set(G1ParScanThreadStateSet* per_thread_states) {
-  G1OptionalCSet optional_cset(&_collection_set, per_thread_states);
-  if (optional_cset.is_empty()) {
-    return;
-  }
-
-  if (evacuation_failed()) {
-    return;
-  }
-
   const double gc_start_time_ms = phase_times()->cur_collection_start_sec() * 1000.0;
 
-  double start_time_sec = os::elapsedTime();
-
-  do {
+  Ticks start = Ticks::now();
+
+  while (!evacuation_failed() && _collection_set.optional_region_length() > 0) {
+
     double time_used_ms = os::elapsedTime() * 1000.0 - gc_start_time_ms;
     double time_left_ms = MaxGCPauseMillis - time_used_ms;
 
-    if (time_left_ms < 0) {
-      log_trace(gc, ergo, cset)("Skipping %u optional regions, pause time exceeded %.3fms", optional_cset.size(), time_used_ms);
+    if (time_left_ms < 0 ||
+        !_collection_set.finalize_optional_for_evacuation(time_left_ms * policy()->optional_evacuation_fraction())) {
+      log_trace(gc, ergo, cset)("Skipping evacuation of %u optional regions, no more regions can be evacuated in %.3fms",
+                                _collection_set.optional_region_length(), time_left_ms);
       break;
     }
 
-    optional_cset.prepare_evacuation(time_left_ms * _policy->optional_evacuation_fraction());
-    if (optional_cset.prepare_failed()) {
-      log_trace(gc, ergo, cset)("Skipping %u optional regions, no regions can be evacuated in %.3fms", optional_cset.size(), time_left_ms);
-      break;
-    }
-
-    evacuate_optional_regions(per_thread_states, &optional_cset);
-
-    optional_cset.complete_evacuation();
-    if (optional_cset.evacuation_failed()) {
-      break;
-    }
-  } while (!optional_cset.is_empty());
-
-  phase_times()->record_optional_evacuation((os::elapsedTime() - start_time_sec) * 1000.0);
+    evacuate_next_optional_regions(per_thread_states);
+  }
+
+  _collection_set.abandon_optional_collection_set(per_thread_states);
+
+  phase_times()->record_or_add_optional_evac_time((Ticks::now() - start).seconds() * 1000.0);
 }
 
 void G1CollectedHeap::post_evacuate_collection_set(G1EvacuationInfo& evacuation_info, G1ParScanThreadStateSet* per_thread_states) {
@@ -4259,15 +4209,14 @@
   double free_cset_start_time = os::elapsedTime();
 
   {
-    uint const num_chunks = MAX2(_collection_set.region_length() / G1FreeCollectionSetTask::chunk_size(), 1U);
+    uint const num_regions = _collection_set.region_length();
+    uint const num_chunks = MAX2(num_regions / G1FreeCollectionSetTask::chunk_size(), 1U);
     uint const num_workers = MIN2(workers()->active_workers(), num_chunks);
 
     G1FreeCollectionSetTask cl(collection_set, &evacuation_info, surviving_young_words);
 
     log_debug(gc, ergo)("Running %s using %u workers for collection set length %u",
-                        cl.name(),
-                        num_workers,
-                        _collection_set.region_length());
+                        cl.name(), num_workers, num_regions);
     workers()->run_task(&cl, num_workers);
   }
   phase_times()->record_total_free_cset_time_ms((os::elapsedTime() - free_cset_start_time) * 1000.0);
@@ -4436,7 +4385,7 @@
 
 void G1CollectedHeap::abandon_collection_set(G1CollectionSet* collection_set) {
   G1AbandonCollectionSetClosure cl;
-  collection_set->iterate(&cl);
+  collection_set_iterate_all(&cl);
 
   collection_set->clear();
   collection_set->stop_incremental_building();
--- a/src/hotspot/share/gc/g1/g1CollectedHeap.hpp	Mon Apr 08 11:11:22 2019 -0700
+++ b/src/hotspot/share/gc/g1/g1CollectedHeap.hpp	Mon Apr 08 20:37:52 2019 +0200
@@ -143,9 +143,8 @@
   // Closures used in implementation.
   friend class G1ParScanThreadState;
   friend class G1ParScanThreadStateSet;
-  friend class G1ParTask;
+  friend class G1EvacuateRegionsTask;
   friend class G1PLABAllocator;
-  friend class G1PrepareCompactClosure;
 
   // Other related classes.
   friend class HeapRegionClaimer;
@@ -519,6 +518,10 @@
 
   WorkGang* workers() const { return _workers; }
 
+  // Runs the given AbstractGangTask with the current active workers, returning the
+  // total time taken.
+  Tickspan run_task(AbstractGangTask* task);
+
   G1Allocator* allocator() {
     return _allocator;
   }
@@ -738,11 +741,14 @@
 
   void calculate_collection_set(G1EvacuationInfo& evacuation_info, double target_pause_time_ms);
 
-  // Actually do the work of evacuating the collection set.
-  void evacuate_collection_set(G1ParScanThreadStateSet* per_thread_states);
+  // Actually do the work of evacuating the parts of the collection set.
+  void evacuate_initial_collection_set(G1ParScanThreadStateSet* per_thread_states);
   void evacuate_optional_collection_set(G1ParScanThreadStateSet* per_thread_states);
-  void evacuate_optional_regions(G1ParScanThreadStateSet* per_thread_states, G1OptionalCSet* ocset);
+private:
+  // Evacuate the next set of optional regions.
+  void evacuate_next_optional_regions(G1ParScanThreadStateSet* per_thread_states);
 
+public:
   void pre_evacuate_collection_set(G1EvacuationInfo& evacuation_info);
   void post_evacuate_collection_set(G1EvacuationInfo& evacuation_info, G1ParScanThreadStateSet* pss);
 
@@ -1165,14 +1171,14 @@
   void heap_region_par_iterate_from_start(HeapRegionClosure* cl,
                                           HeapRegionClaimer* hrclaimer) const;
 
-  // Iterate over the regions (if any) in the current collection set.
-  void collection_set_iterate(HeapRegionClosure* blk);
+  // Iterate over all regions currently in the current collection set.
+  void collection_set_iterate_all(HeapRegionClosure* blk);
 
-  // Iterate over the regions (if any) in the current collection set. Starts the
-  // iteration over the entire collection set so that the start regions of a given
-  // worker id over the set active_workers are evenly spread across the set of
-  // collection set regions.
-  void collection_set_iterate_from(HeapRegionClosure *blk, uint worker_id);
+  // Iterate over the regions in the current increment of the collection set.
+  // Starts the iteration so that the start regions of a given worker id over the
+  // set active_workers are evenly spread across the set of collection set regions
+  // to be iterated.
+  void collection_set_iterate_increment_from(HeapRegionClosure *blk, uint worker_id);
 
   // Returns the HeapRegion that contains addr. addr must not be NULL.
   template <class T>
@@ -1420,7 +1426,7 @@
   size_t _term_attempts;
 
   void start_term_time() { _term_attempts++; _start_term = os::elapsedTime(); }
-  void end_term_time() { _term_time += os::elapsedTime() - _start_term; }
+  void end_term_time() { _term_time += (os::elapsedTime() - _start_term); }
 protected:
   G1CollectedHeap*              _g1h;
   G1ParScanThreadState*         _par_scan_state;
--- a/src/hotspot/share/gc/g1/g1CollectionSet.cpp	Mon Apr 08 11:11:22 2019 -0700
+++ b/src/hotspot/share/gc/g1/g1CollectionSet.cpp	Mon Apr 08 20:37:52 2019 +0200
@@ -59,12 +59,11 @@
   _collection_set_regions(NULL),
   _collection_set_cur_length(0),
   _collection_set_max_length(0),
-  _optional_regions(NULL),
-  _optional_region_length(0),
-  _optional_region_max_length(0),
+  _num_optional_regions(0),
   _bytes_used_before(0),
   _recorded_rs_lengths(0),
   _inc_build_state(Inactive),
+  _inc_part_start(0),
   _inc_bytes_used_before(0),
   _inc_recorded_rs_lengths(0),
   _inc_recorded_rs_lengths_diffs(0),
@@ -90,8 +89,8 @@
   assert((size_t) young_region_length() == _collection_set_cur_length,
          "Young region length %u should match collection set length " SIZE_FORMAT, young_region_length(), _collection_set_cur_length);
 
-  _old_region_length      = 0;
-  _optional_region_length = 0;
+  _old_region_length = 0;
+  free_optional_regions();
 }
 
 void G1CollectionSet::initialize(uint max_region_length) {
@@ -100,21 +99,8 @@
   _collection_set_regions = NEW_C_HEAP_ARRAY(uint, max_region_length, mtGC);
 }
 
-void G1CollectionSet::initialize_optional(uint max_length) {
-  assert(_optional_regions == NULL, "Already initialized");
-  assert(_optional_region_length == 0, "Already initialized");
-  assert(_optional_region_max_length == 0, "Already initialized");
-  _optional_region_max_length = max_length;
-  _optional_regions = NEW_C_HEAP_ARRAY(HeapRegion*, _optional_region_max_length, mtGC);
-}
-
 void G1CollectionSet::free_optional_regions() {
-  _optional_region_length = 0;
-  _optional_region_max_length = 0;
-  if (_optional_regions != NULL) {
-    FREE_C_HEAP_ARRAY(HeapRegion*, _optional_regions);
-    _optional_regions = NULL;
-  }
+  _num_optional_regions = 0;
 }
 
 void G1CollectionSet::clear_candidates() {
@@ -130,39 +116,32 @@
 void G1CollectionSet::add_old_region(HeapRegion* hr) {
   assert_at_safepoint_on_vm_thread();
 
-  assert(_inc_build_state == Active || hr->index_in_opt_cset() != G1OptionalCSet::InvalidCSetIndex,
+  assert(_inc_build_state == Active,
          "Precondition, actively building cset or adding optional later on");
   assert(hr->is_old(), "the region should be old");
 
-  assert(!hr->in_collection_set(), "should not already be in the CSet");
+  assert(!hr->in_collection_set(), "should not already be in the collection set");
   _g1h->register_old_region_with_cset(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.");
 
   _bytes_used_before += hr->used();
-  size_t rs_length = hr->rem_set()->occupied();
-  _recorded_rs_lengths += rs_length;
-  _old_region_length += 1;
+  _recorded_rs_lengths += hr->rem_set()->occupied();
+  _old_region_length++;
 
-  log_trace(gc, cset)("Added old region %d to collection set", hr->hrm_index());
+  _g1h->old_set_remove(hr);
 }
 
 void G1CollectionSet::add_optional_region(HeapRegion* hr) {
-  assert(!optional_is_full(), "Precondition, must have room left for this region");
   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);
 
-  _optional_regions[_optional_region_length] = hr;
-  uint index = _optional_region_length++;
-  hr->set_index_in_opt_cset(index);
-
-  log_trace(gc, cset)("Added region %d to optional collection set (%u)", hr->hrm_index(), _optional_region_length);
+  hr->set_index_in_opt_cset(_num_optional_regions++);
 }
 
-// Initialize the per-collection-set information
 void G1CollectionSet::start_incremental_building() {
   assert(_collection_set_cur_length == 0, "Collection set must be empty before starting a new collection set.");
   assert(_inc_build_state == Inactive, "Precondition");
@@ -173,7 +152,8 @@
   _inc_recorded_rs_lengths_diffs = 0;
   _inc_predicted_elapsed_time_ms = 0.0;
   _inc_predicted_elapsed_time_ms_diffs = 0.0;
-  _inc_build_state = Active;
+
+  update_incremental_marker();
 }
 
 void G1CollectionSet::finalize_incremental_building() {
@@ -211,29 +191,48 @@
 void G1CollectionSet::clear() {
   assert_at_safepoint_on_vm_thread();
   _collection_set_cur_length = 0;
-  _optional_region_length = 0;
 }
 
 void G1CollectionSet::iterate(HeapRegionClosure* cl) const {
-  iterate_from(cl, 0, 1);
-}
-
-void G1CollectionSet::iterate_from(HeapRegionClosure* cl, uint worker_id, uint total_workers) const {
   size_t len = _collection_set_cur_length;
   OrderAccess::loadload();
-  if (len == 0) {
-    return;
-  }
-  size_t start_pos = (worker_id * len) / total_workers;
-  size_t cur_pos = start_pos;
 
-  do {
-    HeapRegion* r = _g1h->region_at(_collection_set_regions[cur_pos]);
+  for (uint i = 0; i < len; i++) {
+    HeapRegion* r = _g1h->region_at(_collection_set_regions[i]);
     bool result = cl->do_heap_region(r);
     if (result) {
       cl->set_incomplete();
       return;
     }
+  }
+}
+
+void G1CollectionSet::iterate_optional(HeapRegionClosure* cl) const {
+  assert_at_safepoint();
+
+  for (uint i = 0; i < _num_optional_regions; i++) {
+    HeapRegion* r = _candidates->at(i);
+    bool result = cl->do_heap_region(r);
+    guarantee(!result, "Must not cancel iteration");
+  }
+}
+
+void G1CollectionSet::iterate_incremental_part_from(HeapRegionClosure* cl, uint worker_id, uint total_workers) const {
+  assert_at_safepoint();
+
+  size_t len = _collection_set_cur_length - _inc_part_start;
+  if (len == 0) {
+    return;
+  }
+
+  size_t start_pos = (worker_id * len) / total_workers;
+  size_t cur_pos = start_pos;
+
+  do {
+    HeapRegion* r = _g1h->region_at(_collection_set_regions[cur_pos + _inc_part_start]);
+    bool result = cl->do_heap_region(r);
+    guarantee(!result, "Must not cancel iteration");
+
     cur_pos++;
     if (cur_pos == len) {
       cur_pos = 0;
@@ -440,30 +439,6 @@
   return time_remaining_ms;
 }
 
-void G1CollectionSet::add_as_old(HeapRegion* hr) {
-  candidates()->pop_front(); // already have region via peek()
-  _g1h->old_set_remove(hr);
-  add_old_region(hr);
-}
-
-void G1CollectionSet::add_as_optional(HeapRegion* hr) {
-  assert(_optional_regions != NULL, "Must not be called before array is allocated");
-  candidates()->pop_front(); // already have region via peek()
-  _g1h->old_set_remove(hr);
-  add_optional_region(hr);
-}
-
-bool G1CollectionSet::optional_is_full() {
-  assert(_optional_region_length <= _optional_region_max_length, "Invariant");
-  return _optional_region_length == _optional_region_max_length;
-}
-
-void G1CollectionSet::clear_optional_region(const HeapRegion* hr) {
-  assert(_optional_regions != NULL, "Must not be called before array is allocated");
-  uint index = hr->index_in_opt_cset();
-  _optional_regions[index] = NULL;
-}
-
 static int compare_region_idx(const uint a, const uint b) {
   if (a > b) {
     return 1;
@@ -476,87 +451,25 @@
 
 void G1CollectionSet::finalize_old_part(double time_remaining_ms) {
   double non_young_start_time_sec = os::elapsedTime();
-  double predicted_old_time_ms = 0.0;
-  double predicted_optional_time_ms = 0.0;
-  double optional_threshold_ms = time_remaining_ms * _policy->optional_prediction_fraction();
-  uint expensive_region_num = 0;
 
   if (collector_state()->in_mixed_phase()) {
     candidates()->verify();
-    const uint min_old_cset_length = _policy->calc_min_old_cset_length();
-    const uint max_old_cset_length = MAX2(min_old_cset_length, _policy->calc_max_old_cset_length());
-    bool check_time_remaining = _policy->adaptive_young_list_length();
 
-    initialize_optional(max_old_cset_length - min_old_cset_length);
-    log_debug(gc, ergo, cset)("Start adding old regions for mixed gc. min %u regions, max %u regions, "
-                              "time remaining %1.2fms, optional threshold %1.2fms",
-                              min_old_cset_length, max_old_cset_length, time_remaining_ms, optional_threshold_ms);
+    uint num_initial_old_regions;
+    uint num_optional_old_regions;
 
-    HeapRegion* hr = candidates()->peek_front();
-    while (hr != NULL) {
-      if (old_region_length() + optional_region_length() >= max_old_cset_length) {
-        // Added maximum number of old regions to the CSet.
-        log_debug(gc, ergo, cset)("Finish adding old regions to CSet (old CSet region num reached max). "
-                                  "old %u regions, optional %u regions",
-                                  old_region_length(), optional_region_length());
-        break;
-      }
+    _policy->calculate_old_collection_set_regions(candidates(),
+                                                  time_remaining_ms,
+                                                  num_initial_old_regions,
+                                                  num_optional_old_regions);
 
-      // Stop adding regions if the remaining reclaimable space is
-      // not above G1HeapWastePercent.
-      size_t reclaimable_bytes = candidates()->remaining_reclaimable_bytes();
-      double reclaimable_percent = _policy->reclaimable_bytes_percent(reclaimable_bytes);
-      double threshold = (double) G1HeapWastePercent;
-      if (reclaimable_percent <= threshold) {
-        // We've added enough old regions that the amount of uncollected
-        // reclaimable space is at or below the waste threshold. Stop
-        // adding old regions to the CSet.
-        log_debug(gc, ergo, cset)("Finish adding old regions to CSet (reclaimable percentage not over threshold). "
-                                  "reclaimable: " SIZE_FORMAT "%s (%1.2f%%) threshold: " UINTX_FORMAT "%%",
-                                  byte_size_in_proper_unit(reclaimable_bytes), proper_unit_for_byte_size(reclaimable_bytes),
-                                  reclaimable_percent, G1HeapWastePercent);
-        break;
-      }
+    // Prepare initial old regions.
+    move_candidates_to_collection_set(num_initial_old_regions);
 
-      double predicted_time_ms = predict_region_elapsed_time_ms(hr);
-      time_remaining_ms = MAX2(time_remaining_ms - predicted_time_ms, 0.0);
-      // Add regions to old set until we reach minimum amount
-      if (old_region_length() < min_old_cset_length) {
-        predicted_old_time_ms += predicted_time_ms;
-        add_as_old(hr);
-        // Record the number of regions added when no time remaining
-        if (time_remaining_ms == 0.0) {
-          expensive_region_num++;
-        }
-      } else {
-        // In the non-auto-tuning case, we'll finish adding regions
-        // to the CSet if we reach the minimum.
-        if (!check_time_remaining) {
-          log_debug(gc, ergo, cset)("Finish adding old regions to CSet (old CSet region num reached min).");
-          break;
-        }
-        // Keep adding regions to old set until we reach optional threshold
-        if (time_remaining_ms > optional_threshold_ms) {
-          predicted_old_time_ms += predicted_time_ms;
-          add_as_old(hr);
-        } else if (time_remaining_ms > 0) {
-          // Keep adding optional regions until time is up
-          if (!optional_is_full()) {
-            predicted_optional_time_ms += predicted_time_ms;
-            add_as_optional(hr);
-          } else {
-            log_debug(gc, ergo, cset)("Finish adding old regions to CSet (optional set full).");
-            break;
-          }
-        } else {
-          log_debug(gc, ergo, cset)("Finish adding old regions to CSet (predicted time is too high).");
-          break;
-        }
-      }
-      hr = candidates()->peek_front();
-    }
-    if (hr == NULL) {
-      log_debug(gc, ergo, cset)("Finish adding old regions to CSet (candidate old regions not available)");
+    // Prepare optional old regions for evacuation.
+    uint candidate_idx = candidates()->cur_idx();
+    for (uint i = 0; i < num_optional_old_regions; i++) {
+      add_optional_region(candidates()->at(candidate_idx + i));
     }
 
     candidates()->verify();
@@ -564,99 +477,59 @@
 
   stop_incremental_building();
 
-  log_debug(gc, ergo, cset)("Finish choosing CSet regions old: %u, optional: %u, "
-                            "predicted old time: %1.2fms, predicted optional time: %1.2fms, time remaining: %1.2f",
-                            old_region_length(), optional_region_length(),
-                            predicted_old_time_ms, predicted_optional_time_ms, time_remaining_ms);
-  if (expensive_region_num > 0) {
-    log_debug(gc, ergo, cset)("CSet contains %u old regions that were added although the predicted time was too high.",
-                              expensive_region_num);
-  }
-
   double non_young_end_time_sec = os::elapsedTime();
   phase_times()->record_non_young_cset_choice_time_ms((non_young_end_time_sec - non_young_start_time_sec) * 1000.0);
 
   QuickSort::sort(_collection_set_regions, _collection_set_cur_length, compare_region_idx, true);
 }
 
-HeapRegion* G1OptionalCSet::region_at(uint index) {
-  return _cset->optional_region_at(index);
+void G1CollectionSet::move_candidates_to_collection_set(uint num_old_candidate_regions) {
+  if (num_old_candidate_regions == 0) {
+    return;
+  }
+  uint candidate_idx = candidates()->cur_idx();
+  for (uint i = 0; i < num_old_candidate_regions; i++) {
+    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);
+    add_old_region(r);
+  }
+  candidates()->remove(num_old_candidate_regions);
+
+  candidates()->verify();
 }
 
-void G1OptionalCSet::prepare_evacuation(double time_limit) {
-  assert(_current_index == _current_limit, "Before prepare no regions should be ready for evac");
-
-  uint prepared_regions = 0;
-  double prediction_ms = 0;
-
-  _prepare_failed = true;
-  for (uint i = _current_index; i < _cset->optional_region_length(); i++) {
-    HeapRegion* hr = region_at(i);
-    prediction_ms += _cset->predict_region_elapsed_time_ms(hr);
-    if (prediction_ms > time_limit) {
-      log_debug(gc, cset)("Prepared %u regions for optional evacuation. Predicted time: %.3fms", prepared_regions, prediction_ms);
-      return;
-    }
-
-    // This region will be included in the next optional evacuation.
-    prepare_to_evacuate_optional_region(hr);
-    prepared_regions++;
-    _current_limit++;
-    _prepare_failed = false;
-  }
-
-  log_debug(gc, cset)("Prepared all %u regions for optional evacuation. Predicted time: %.3fms",
-                      prepared_regions, prediction_ms);
+void G1CollectionSet::finalize_initial_collection_set(double target_pause_time_ms, G1SurvivorRegions* survivor) {
+  double time_remaining_ms = finalize_young_part(target_pause_time_ms, survivor);
+  finalize_old_part(time_remaining_ms);
 }
 
-bool G1OptionalCSet::prepare_failed() {
-  return _prepare_failed;
+bool G1CollectionSet::finalize_optional_for_evacuation(double remaining_pause_time) {
+  update_incremental_marker();
+
+  uint num_selected_regions;
+  _policy->calculate_optional_collection_set_regions(candidates(),
+                                                     _num_optional_regions,
+                                                     remaining_pause_time,
+                                                     num_selected_regions);
+
+  move_candidates_to_collection_set(num_selected_regions);
+
+  _num_optional_regions -= num_selected_regions;
+
+  stop_incremental_building();
+  return num_selected_regions > 0;
 }
 
-void G1OptionalCSet::complete_evacuation() {
-  _evacuation_failed = false;
-  for (uint i = _current_index; i < _current_limit; i++) {
-    HeapRegion* hr = region_at(i);
-    _cset->clear_optional_region(hr);
-    if (hr->evacuation_failed()){
-      _evacuation_failed = true;
-    }
+void G1CollectionSet::abandon_optional_collection_set(G1ParScanThreadStateSet* pss) {
+  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);
+    r->clear_index_in_opt_cset();
   }
-  _current_index = _current_limit;
-}
-
-bool G1OptionalCSet::evacuation_failed() {
-  return _evacuation_failed;
-}
-
-G1OptionalCSet::~G1OptionalCSet() {
-  G1CollectedHeap* g1h = G1CollectedHeap::heap();
-  while (!is_empty()) {
-    // We want to return regions not evacuated to the collection set candidates
-    // in reverse order to maintain the old order.
-    HeapRegion* hr = _cset->remove_last_optional_region();
-    assert(hr != NULL, "Should be valid region left");
-    _pset->record_unused_optional_region(hr);
-    g1h->old_set_add(hr);
-    g1h->clear_in_cset(hr);
-    hr->set_index_in_opt_cset(InvalidCSetIndex);
-    _cset->candidates()->push_front(hr);
-  }
-  _cset->free_optional_regions();
-}
-
-uint G1OptionalCSet::size() {
-  return _cset->optional_region_length() - _current_index;
-}
-
-bool G1OptionalCSet::is_empty() {
-  return size() == 0;
-}
-
-void G1OptionalCSet::prepare_to_evacuate_optional_region(HeapRegion* hr) {
-  log_trace(gc, cset)("Adding region %u for optional evacuation", hr->hrm_index());
-  G1CollectedHeap::heap()->clear_in_cset(hr);
-  _cset->add_old_region(hr);
+  free_optional_regions();
 }
 
 #ifdef ASSERT
--- a/src/hotspot/share/gc/g1/g1CollectionSet.hpp	Mon Apr 08 11:11:22 2019 -0700
+++ b/src/hotspot/share/gc/g1/g1CollectionSet.hpp	Mon Apr 08 20:37:52 2019 +0200
@@ -38,11 +38,101 @@
 class HeapRegion;
 class HeapRegionClosure;
 
+// The collection set.
+//
+// The set of regions that are evacuated during an evacuation pause.
+//
+// At the end of a collection, before freeing the collection set, this set
+// contains all regions that were evacuated during this collection:
+//
+// - survivor regions from the last collection (if any)
+// - eden regions allocated by the mutator
+// - old gen regions evacuated during mixed gc
+//
+// This set is built incrementally at mutator time as regions are retired, and
+// if this had been a mixed gc, some additional (during gc) incrementally added
+// old regions from the collection set candidates built during the concurrent
+// cycle.
+//
+// A more detailed overview of how the collection set changes over time follows:
+//
+// 0) at the end of GC the survivor regions are added to this collection set.
+// 1) the mutator incrementally adds eden regions as they retire
+//
+// ----- gc starts
+//
+// 2) prepare (finalize) young regions of the collection set for collection
+//    - relabel the survivors as eden
+//    - finish up the incremental building that happened at mutator time
+//
+// iff this is a young-only collection:
+//
+// a3) evacuate the current collection set in one "initial evacuation" phase
+//
+// iff this is a mixed collection:
+//
+// b3) calculate the set of old gen regions we may be able to collect in this
+//     collection from the list of collection set candidates.
+//     - one part is added to the current collection set
+//     - the remainder regions are labeled as optional, and NOT yet added to the
+//     collection set.
+// b4) evacuate the current collection set in the "initial evacuation" phase
+// b5) evacuate the optional regions in the "optional evacuation" phase. This is
+//     done in increments (or rounds).
+//     b5-1) add a few of the optional regions to the current collection set
+//     b5-2) evacuate only these newly added optional regions. For this mechanism we
+//     reuse the incremental collection set building infrastructure (used also at
+//     mutator time).
+//     b5-3) repeat from b5-1 until the policy determines we are done
+//
+// all collections
+//
+// 6) free the collection set (contains all regions now; empties collection set
+//    afterwards)
+// 7) add survivors to this collection set
+//
+// ----- gc ends
+//
+// goto 1)
+//
+// Examples of how the collection set might look over time:
+//
+// Legend:
+// S = survivor, E = eden, O = old.
+// |xxxx| = increment (with increment markers), containing four regions
+//
+// |SSSS|                         ... after step 0), with four survivor regions
+// |SSSSEE|                       ... at step 1), after retiring two eden regions
+// |SSSSEEEE|                     ... after step 1), after retiring four eden regions
+// |EEEEEEEE|                     ... after step 2)
+//
+// iff this is a young-only collection
+//
+// EEEEEEEE||                      ... after step a3), after initial evacuation phase
+// ||                              ... after step 6)
+// |SS|                            ... after step 7), with two survivor regions
+//
+// iff this is a mixed collection
+//
+// |EEEEEEEEOOOO|                  ... after step b3), added four regions to be
+//                                     evacuated in the "initial evacuation" phase
+// EEEEEEEEOOOO||                  ... after step b4), incremental part is empty
+//                                     after evacuation
+// EEEEEEEEOOOO|OO|                ... after step b5.1), added two regions to be
+//                                     evacuated in the first round of the
+//                                     "optional evacuation" phase
+// EEEEEEEEOOOOOO|O|               ... after step b5.1), added one region to be
+//                                     evacuated in the second round of the
+//                                     "optional evacuation" phase
+// EEEEEEEEOOOOOOO||               ... after step b5), the complete collection set.
+// ||                              ... after step b6)
+// |SSS|                           ... after step 7), with three survivor regions
+//
 class G1CollectionSet {
   G1CollectedHeap* _g1h;
   G1Policy* _policy;
 
-  // All old gen collection set candidate regions for the current mixed gc phase.
+  // All old gen collection set candidate regions for the current mixed phase.
   G1CollectionSetCandidates* _candidates;
 
   uint _eden_region_length;
@@ -51,7 +141,7 @@
 
   // The actual collection set as a set of region indices.
   // All entries in _collection_set_regions below _collection_set_cur_length are
-  // assumed to be valid entries.
+  // assumed to be part of the collection set.
   // We assume that at any time there is at most only one writer and (one or more)
   // concurrent readers. This means we are good with using storestore and loadload
   // barriers on the writer and reader respectively only.
@@ -59,31 +149,33 @@
   volatile size_t _collection_set_cur_length;
   size_t _collection_set_max_length;
 
-  // When doing mixed collections we can add old regions to the collection, which
-  // can be collected if there is enough time. We call these optional regions and
-  // the pointer to these regions are stored in the array below.
-  HeapRegion** _optional_regions;
-  uint _optional_region_length;
-  uint _optional_region_max_length;
+  // When doing mixed collections we can add old regions to the collection set, which
+  // will be collected only if there is enough time. We call these optional regions.
+  // This member records the current number of regions that are of that type that
+  // correspond to the first x entries in the collection set candidates.
+  uint _num_optional_regions;
 
   // The number of bytes in the collection set before the pause. Set from
   // the incrementally built collection set at the start of an evacuation
-  // pause, and incremented in finalize_old_part() when adding old regions
-  // (if any) to the collection set.
+  // pause, and updated as more regions are added to the collection set.
   size_t _bytes_used_before;
 
+  // The number of cards in the remembered set in the collection set. Set from
+  // the incrementally built collection set at the start of an evacuation
+  // pause, and updated as more regions are added to the collection set.
   size_t _recorded_rs_lengths;
 
-  // The associated information that is maintained while the incremental
-  // collection set is being built with young regions. Used to populate
-  // the recorded info for the evacuation pause.
-
   enum CSetBuildType {
     Active,             // We are actively building the collection set
     Inactive            // We are not actively building the collection set
   };
 
   CSetBuildType _inc_build_state;
+  size_t _inc_part_start;
+
+  // The associated information that is maintained while the incremental
+  // collection set is being built with *young* regions. Used to populate
+  // the recorded info for the evacuation pause.
 
   // The number of bytes in the incrementally built collection set.
   // Used to set _collection_set_bytes_used_before at the start of
@@ -113,22 +205,44 @@
   // See the comment for _inc_recorded_rs_lengths_diffs.
   double _inc_predicted_elapsed_time_ms_diffs;
 
+  void set_recorded_rs_lengths(size_t rs_lengths);
+
   G1CollectorState* collector_state();
   G1GCPhaseTimes* phase_times();
 
   void verify_young_cset_indices() const NOT_DEBUG_RETURN;
-  void add_as_optional(HeapRegion* hr);
-  void add_as_old(HeapRegion* hr);
-  bool optional_is_full();
 
+  double predict_region_elapsed_time_ms(HeapRegion* hr);
+
+  // Update the incremental collection set information when adding a region.
+  void add_young_region_common(HeapRegion* hr);
+
+  // Add old region "hr" to the collection set.
+  void add_old_region(HeapRegion* hr);
+  void free_optional_regions();
+
+  // Add old region "hr" to optional collection set.
+  void add_optional_region(HeapRegion* hr);
+
+  void move_candidates_to_collection_set(uint num_regions);
+
+  // Finalize the young part of the initial collection set. Relabel survivor regions
+  // as Eden and calculate a prediction on how long the evacuation of all young regions
+  // will take.
+  double finalize_young_part(double target_pause_time_ms, G1SurvivorRegions* survivors);
+  // Perform any final calculations on the incremental collection set fields before we
+  // can use them.
+  void finalize_incremental_building();
+
+  // Select the old regions of the initial collection set and determine how many optional
+  // regions we might be able to evacuate in this pause.
+  void finalize_old_part(double time_remaining_ms);
 public:
   G1CollectionSet(G1CollectedHeap* g1h, G1Policy* policy);
   ~G1CollectionSet();
 
   // Initializes the collection set giving the maximum possible length of the collection set.
   void initialize(uint max_region_length);
-  void initialize_optional(uint max_length);
-  void free_optional_regions();
 
   void clear_candidates();
 
@@ -141,8 +255,6 @@
   void init_region_lengths(uint eden_cset_region_length,
                            uint survivor_cset_region_length);
 
-  void set_recorded_rs_lengths(size_t rs_lengths);
-
   uint region_length() const       { return young_region_length() +
                                             old_region_length(); }
   uint young_region_length() const { return eden_region_length() +
@@ -151,32 +263,29 @@
   uint eden_region_length() const     { return _eden_region_length;     }
   uint survivor_region_length() const { return _survivor_region_length; }
   uint old_region_length() const      { return _old_region_length;      }
-  uint optional_region_length() const { return _optional_region_length; }
+  uint optional_region_length() const { return _num_optional_regions; }
+
+  // Reset the contents of the collection set.
+  void clear();
 
   // Incremental collection set support
 
   // Initialize incremental collection set info.
   void start_incremental_building();
+  // Start a new collection set increment.
+  void update_incremental_marker() { _inc_build_state = Active; _inc_part_start = _collection_set_cur_length; }
+  // Stop adding regions to the current collection set increment.
+  void stop_incremental_building() { _inc_build_state = Inactive; }
 
-  // Perform any final calculations on the incremental collection set fields
-  // before we can use them.
-  void finalize_incremental_building();
+  // Iterate over the current collection set increment applying the given HeapRegionClosure
+  // from a starting position determined by the given worker id.
+  void iterate_incremental_part_from(HeapRegionClosure* cl, uint worker_id, uint total_workers) const;
 
-  // Reset the contents of the collection set.
-  void clear();
-
-  // Iterate over the collection set, applying the given HeapRegionClosure on all of them.
-  // If may_be_aborted is true, iteration may be aborted using the return value of the
-  // called closure method.
+  // Iterate over the entire collection set (all increments calculated so far), applying
+  // the given HeapRegionClosure on all of them.
   void iterate(HeapRegionClosure* cl) const;
 
-  // Iterate over the collection set, applying the given HeapRegionClosure on all of them,
-  // trying to optimally spread out starting position of total_workers workers given the
-  // caller's worker_id.
-  void iterate_from(HeapRegionClosure* cl, uint worker_id, uint total_workers) const;
-
-  // Stop adding regions to the incremental collection set.
-  void stop_incremental_building() { _inc_build_state = Inactive; }
+  void iterate_optional(HeapRegionClosure* cl) const;
 
   size_t recorded_rs_lengths() { return _recorded_rs_lengths; }
 
@@ -188,16 +297,14 @@
     _bytes_used_before = 0;
   }
 
-  // Choose a new collection set.  Marks the chosen regions as being
-  // "in_collection_set".
-  double finalize_young_part(double target_pause_time_ms, G1SurvivorRegions* survivors);
-  void finalize_old_part(double time_remaining_ms);
-
-  // Add old region "hr" to the collection set.
-  void add_old_region(HeapRegion* hr);
-
-  // Add old region "hr" to optional collection set.
-  void add_optional_region(HeapRegion* hr);
+  // Finalize the initial collection set consisting of all young regions potentially a
+  // few old gen regions.
+  void finalize_initial_collection_set(double target_pause_time_ms, G1SurvivorRegions* survivor);
+  // Finalize the next collection set from the set of available optional old gen regions.
+  bool finalize_optional_for_evacuation(double remaining_pause_time);
+  // Abandon (clean up) optional collection set regions that were not evacuated in this
+  // pause.
+  void abandon_optional_collection_set(G1ParScanThreadStateSet* pss);
 
   // Update information about hr in the aggregated information for
   // the incrementally built collection set.
@@ -214,73 +321,6 @@
 
   void print(outputStream* st);
 #endif // !PRODUCT
-
-  double predict_region_elapsed_time_ms(HeapRegion* hr);
-
-  void clear_optional_region(const HeapRegion* hr);
-
-  HeapRegion* optional_region_at(uint i) const {
-    assert(_optional_regions != NULL, "Not yet initialized");
-    assert(i < _optional_region_length, "index %u out of bounds (%u)", i, _optional_region_length);
-    return _optional_regions[i];
-  }
-
-  HeapRegion* remove_last_optional_region() {
-    assert(_optional_regions != NULL, "Not yet initialized");
-    assert(_optional_region_length != 0, "No region to remove");
-    _optional_region_length--;
-    HeapRegion* removed = _optional_regions[_optional_region_length];
-    _optional_regions[_optional_region_length] = NULL;
-    return removed;
-  }
-
-private:
-  // Update the incremental collection set information when adding a region.
-  void add_young_region_common(HeapRegion* hr);
-};
-
-// Helper class to manage the optional regions in a Mixed collection.
-class G1OptionalCSet : public StackObj {
-private:
-  G1CollectionSet* _cset;
-  G1ParScanThreadStateSet* _pset;
-  uint _current_index;
-  uint _current_limit;
-  bool _prepare_failed;
-  bool _evacuation_failed;
-
-  void prepare_to_evacuate_optional_region(HeapRegion* hr);
-
-public:
-  static const uint InvalidCSetIndex = UINT_MAX;
-
-  G1OptionalCSet(G1CollectionSet* cset, G1ParScanThreadStateSet* pset) :
-    _cset(cset),
-    _pset(pset),
-    _current_index(0),
-    _current_limit(0),
-    _prepare_failed(false),
-    _evacuation_failed(false) { }
-  // The destructor returns regions to the collection set candidates set and
-  // frees the optional structure in the collection set.
-  ~G1OptionalCSet();
-
-  uint current_index() { return _current_index; }
-  uint current_limit() { return _current_limit; }
-
-  uint size();
-  bool is_empty();
-
-  HeapRegion* region_at(uint index);
-
-  // Prepare a set of regions for optional evacuation.
-  void prepare_evacuation(double time_left_ms);
-  bool prepare_failed();
-
-  // Complete the evacuation of the previously prepared
-  // regions by updating their state and check for failures.
-  void complete_evacuation();
-  bool evacuation_failed();
 };
 
 #endif // SHARE_GC_G1_G1COLLECTIONSET_HPP
--- a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp	Mon Apr 08 11:11:22 2019 -0700
+++ b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.cpp	Mon Apr 08 20:37:52 2019 +0200
@@ -27,26 +27,12 @@
 #include "gc/g1/g1CollectionSetChooser.hpp"
 #include "gc/g1/heapRegion.inline.hpp"
 
-HeapRegion* G1CollectionSetCandidates::pop_front() {
-  assert(_front_idx < _num_regions, "pre-condition");
-  HeapRegion* hr = _regions[_front_idx];
-  assert(hr != NULL, "pre-condition");
-  _regions[_front_idx] = NULL;
-  assert(hr->reclaimable_bytes() <= _remaining_reclaimable_bytes,
-         "Remaining reclaimable bytes inconsistent "
-         "from region: " SIZE_FORMAT " remaining: " SIZE_FORMAT,
-         hr->reclaimable_bytes(), _remaining_reclaimable_bytes);
-  _remaining_reclaimable_bytes -= hr->reclaimable_bytes();
-  _front_idx++;
-  return hr;
-}
-
-void G1CollectionSetCandidates::push_front(HeapRegion* hr) {
-  assert(hr != NULL, "Can't put back a NULL region");
-  assert(_front_idx >= 1, "Too many regions have been put back.");
-  _front_idx--;
-  _regions[_front_idx] = hr;
-  _remaining_reclaimable_bytes += hr->reclaimable_bytes();
+void G1CollectionSetCandidates::remove(uint num_regions) {
+  assert(num_regions <= num_remaining(), "Trying to remove more regions (%u) than available (%u)", num_regions, num_remaining());
+  for (uint i = 0; i < num_regions; i++) {
+    _remaining_reclaimable_bytes -= at(_front_idx)->reclaimable_bytes();
+    _front_idx++;
+  }
 }
 
 void G1CollectionSetCandidates::iterate(HeapRegionClosure* cl) {
@@ -62,13 +48,8 @@
 #ifndef PRODUCT
 void G1CollectionSetCandidates::verify() const {
   guarantee(_front_idx <= _num_regions, "Index: %u Num_regions: %u", _front_idx, _num_regions);
-  uint idx = 0;
+  uint idx = _front_idx;
   size_t sum_of_reclaimable_bytes = 0;
-  while (idx < _front_idx) {
-    guarantee(_regions[idx] == NULL, "All entries before _front_idx %u should be NULL, but %u is not",
-              _front_idx, idx);
-    idx++;
-  }
   HeapRegion *prev = NULL;
   for (; idx < _num_regions; idx++) {
     HeapRegion *cur = _regions[idx];
--- a/src/hotspot/share/gc/g1/g1CollectionSetCandidates.hpp	Mon Apr 08 11:11:22 2019 -0700
+++ b/src/hotspot/share/gc/g1/g1CollectionSetCandidates.hpp	Mon Apr 08 20:37:52 2019 +0200
@@ -63,22 +63,18 @@
   // Returns the total number of collection set candidate old regions added.
   uint num_regions() { return _num_regions; }
 
-  // Return the candidate region at the cursor position to be considered for collection without
-  // removing it.
-  HeapRegion* peek_front() {
+  uint cur_idx() const { return _front_idx; }
+
+  HeapRegion* at(uint idx) const {
     HeapRegion* res = NULL;
-    if (_front_idx < _num_regions) {
-      res = _regions[_front_idx];
-      assert(res != NULL, "Unexpected NULL HeapRegion at index %u", _front_idx);
+    if (idx < _num_regions) {
+      res = _regions[idx];
+      assert(res != NULL, "Unexpected NULL HeapRegion at index %u", idx);
     }
     return res;
   }
 
-  // Remove the given region from the candidates set and move the cursor to the next one.
-  HeapRegion* pop_front();
-
-  // Add the given HeapRegion to the front of the collection set candidate set again.
-  void push_front(HeapRegion* hr);
+  void remove(uint num_regions);
 
   // Iterate over all remaining collection set candidate regions.
   void iterate(HeapRegionClosure* cl);
--- a/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp	Mon Apr 08 11:11:22 2019 -0700
+++ b/src/hotspot/share/gc/g1/g1ConcurrentMark.cpp	Mon Apr 08 20:37:52 2019 +0200
@@ -1940,9 +1940,10 @@
     guarantee(oopDesc::is_oop(task_entry.obj()),
               "Non-oop " PTR_FORMAT ", phase: %s, info: %d",
               p2i(task_entry.obj()), _phase, _info);
-    guarantee(!_g1h->is_in_cset(task_entry.obj()),
-              "obj: " PTR_FORMAT " in CSet, phase: %s, info: %d",
-              p2i(task_entry.obj()), _phase, _info);
+    HeapRegion* r = _g1h->heap_region_containing(task_entry.obj());
+    guarantee(!(r->in_collection_set() || r->has_index_in_opt_cset()),
+              "obj " PTR_FORMAT " from %s (%d) in region %u in (optional) collection set",
+              p2i(task_entry.obj()), _phase, _info, r->hrm_index());
   }
 };
 
@@ -1979,11 +1980,11 @@
     HeapWord* task_finger = task->finger();
     if (task_finger != NULL && task_finger < _heap.end()) {
       // See above note on the global finger verification.
-      HeapRegion* task_hr = _g1h->heap_region_containing(task_finger);
-      guarantee(task_hr == NULL || task_finger == task_hr->bottom() ||
-                !task_hr->in_collection_set(),
+      HeapRegion* r = _g1h->heap_region_containing(task_finger);
+      guarantee(r == NULL || task_finger == r->bottom() ||
+                !r->in_collection_set() || !r->has_index_in_opt_cset(),
                 "task finger: " PTR_FORMAT " region: " HR_FORMAT,
-                p2i(task_finger), HR_FORMAT_PARAMS(task_hr));
+                p2i(task_finger), HR_FORMAT_PARAMS(r));
     }
   }
 }
--- a/src/hotspot/share/gc/g1/g1EvacFailure.cpp	Mon Apr 08 11:11:22 2019 -0700
+++ b/src/hotspot/share/gc/g1/g1EvacFailure.cpp	Mon Apr 08 20:37:52 2019 +0200
@@ -228,6 +228,8 @@
 
     if (_hrclaimer->claim_region(hr->hrm_index())) {
       if (hr->evacuation_failed()) {
+        hr->clear_index_in_opt_cset();
+
         bool during_initial_mark = _g1h->collector_state()->in_initial_mark_gc();
         bool during_conc_mark = _g1h->collector_state()->mark_or_rebuild_in_progress();
 
@@ -257,5 +259,5 @@
 void G1ParRemoveSelfForwardPtrsTask::work(uint worker_id) {
   RemoveSelfForwardPtrHRClosure rsfp_cl(worker_id, &_hrclaimer);
 
-  _g1h->collection_set_iterate_from(&rsfp_cl, worker_id);
+  _g1h->collection_set_iterate_increment_from(&rsfp_cl, worker_id);
 }
--- a/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp	Mon Apr 08 11:11:22 2019 -0700
+++ b/src/hotspot/share/gc/g1/g1GCPhaseTimes.cpp	Mon Apr 08 20:37:52 2019 +0200
@@ -76,10 +76,12 @@
   }
   _gc_par_phases[ScanRS] = new WorkerDataArray<double>(max_gc_threads, "Scan RS (ms):");
   _gc_par_phases[OptScanRS] = new WorkerDataArray<double>(max_gc_threads, "Optional Scan RS (ms):");
-  _gc_par_phases[CodeRoots] = new WorkerDataArray<double>(max_gc_threads, "Code Root Scanning (ms):");
+  _gc_par_phases[CodeRoots] = new WorkerDataArray<double>(max_gc_threads, "Code Root Scan (ms):");
+  _gc_par_phases[OptCodeRoots] = new WorkerDataArray<double>(max_gc_threads, "Optional Code Root Scan (ms):");
   _gc_par_phases[ObjCopy] = new WorkerDataArray<double>(max_gc_threads, "Object Copy (ms):");
   _gc_par_phases[OptObjCopy] = new WorkerDataArray<double>(max_gc_threads, "Optional Object Copy (ms):");
   _gc_par_phases[Termination] = new WorkerDataArray<double>(max_gc_threads, "Termination (ms):");
+  _gc_par_phases[OptTermination] = new WorkerDataArray<double>(max_gc_threads, "Optional Termination (ms):");
   _gc_par_phases[GCWorkerTotal] = new WorkerDataArray<double>(max_gc_threads, "GC Worker Total (ms):");
   _gc_par_phases[GCWorkerEnd] = new WorkerDataArray<double>(max_gc_threads, "GC Worker End (ms):");
   _gc_par_phases[Other] = new WorkerDataArray<double>(max_gc_threads, "GC Worker Other (ms):");
@@ -91,14 +93,16 @@
   _scan_rs_skipped_cards = new WorkerDataArray<size_t>(max_gc_threads, "Skipped Cards:");
   _gc_par_phases[ScanRS]->link_thread_work_items(_scan_rs_skipped_cards, ScanRSSkippedCards);
 
-  _opt_cset_scanned_cards = new WorkerDataArray<size_t>(max_gc_threads, "Scanned Cards:");
-  _gc_par_phases[OptScanRS]->link_thread_work_items(_opt_cset_scanned_cards, OptCSetScannedCards);
-  _opt_cset_claimed_cards = new WorkerDataArray<size_t>(max_gc_threads, "Claimed Cards:");
-  _gc_par_phases[OptScanRS]->link_thread_work_items(_opt_cset_claimed_cards, OptCSetClaimedCards);
-  _opt_cset_skipped_cards = new WorkerDataArray<size_t>(max_gc_threads, "Skipped Cards:");
-  _gc_par_phases[OptScanRS]->link_thread_work_items(_opt_cset_skipped_cards, OptCSetSkippedCards);
-  _opt_cset_used_memory = new WorkerDataArray<size_t>(max_gc_threads, "Used Memory:");
-  _gc_par_phases[OptScanRS]->link_thread_work_items(_opt_cset_used_memory, OptCSetUsedMemory);
+  _opt_scan_rs_scanned_cards = new WorkerDataArray<size_t>(max_gc_threads, "Scanned Cards:");
+  _gc_par_phases[OptScanRS]->link_thread_work_items(_opt_scan_rs_scanned_cards, ScanRSScannedCards);
+  _opt_scan_rs_claimed_cards = new WorkerDataArray<size_t>(max_gc_threads, "Claimed Cards:");
+  _gc_par_phases[OptScanRS]->link_thread_work_items(_opt_scan_rs_claimed_cards, ScanRSClaimedCards);
+  _opt_scan_rs_skipped_cards = new WorkerDataArray<size_t>(max_gc_threads, "Skipped Cards:");
+  _gc_par_phases[OptScanRS]->link_thread_work_items(_opt_scan_rs_skipped_cards, ScanRSSkippedCards);
+  _opt_scan_rs_scanned_opt_refs = new WorkerDataArray<size_t>(max_gc_threads, "Scanned Refs:");
+  _gc_par_phases[OptScanRS]->link_thread_work_items(_opt_scan_rs_scanned_opt_refs, ScanRSScannedOptRefs);
+  _opt_scan_rs_used_memory = new WorkerDataArray<size_t>(max_gc_threads, "Used Memory:");
+  _gc_par_phases[OptScanRS]->link_thread_work_items(_opt_scan_rs_used_memory, ScanRSUsedMemory);
 
   _update_rs_processed_buffers = new WorkerDataArray<size_t>(max_gc_threads, "Processed Buffers:");
   _gc_par_phases[UpdateRS]->link_thread_work_items(_update_rs_processed_buffers, UpdateRSProcessedBuffers);
@@ -112,9 +116,17 @@
   _obj_copy_lab_undo_waste = new WorkerDataArray<size_t>(max_gc_threads, "LAB Undo Waste");
   _gc_par_phases[ObjCopy]->link_thread_work_items(_obj_copy_lab_undo_waste, ObjCopyLABUndoWaste);
 
+  _opt_obj_copy_lab_waste = new WorkerDataArray<size_t>(max_gc_threads, "LAB Waste");
+  _gc_par_phases[OptObjCopy]->link_thread_work_items(_obj_copy_lab_waste, ObjCopyLABWaste);
+  _opt_obj_copy_lab_undo_waste  = new WorkerDataArray<size_t>(max_gc_threads, "LAB Undo Waste");
+  _gc_par_phases[OptObjCopy]->link_thread_work_items(_obj_copy_lab_undo_waste, ObjCopyLABUndoWaste);
+
   _termination_attempts = new WorkerDataArray<size_t>(max_gc_threads, "Termination Attempts:");
   _gc_par_phases[Termination]->link_thread_work_items(_termination_attempts);
 
+  _opt_termination_attempts = new WorkerDataArray<size_t>(max_gc_threads, "Optional Termination Attempts:");
+  _gc_par_phases[OptTermination]->link_thread_work_items(_opt_termination_attempts);
+
   if (UseStringDeduplication) {
     _gc_par_phases[StringDedupQueueFixup] = new WorkerDataArray<double>(max_gc_threads, "Queue Fixup (ms):");
     _gc_par_phases[StringDedupTableFixup] = new WorkerDataArray<double>(max_gc_threads, "Table Fixup (ms):");
@@ -134,7 +146,7 @@
 }
 
 void G1GCPhaseTimes::reset() {
-  _cur_collection_par_time_ms = 0.0;
+  _cur_collection_initial_evac_time_ms = 0.0;
   _cur_optional_evac_ms = 0.0;
   _cur_collection_code_root_fixup_time_ms = 0.0;
   _cur_strong_code_root_purge_time_ms = 0.0;
@@ -251,6 +263,10 @@
   }
 }
 
+double G1GCPhaseTimes::get_time_secs(GCParPhases phase, uint worker_i) {
+  return _gc_par_phases[phase]->get(worker_i);
+}
+
 void G1GCPhaseTimes::record_thread_work_item(GCParPhases phase, uint worker_i, size_t count, uint index) {
   _gc_par_phases[phase]->set_thread_work_item(worker_i, count, index);
 }
@@ -259,6 +275,10 @@
   _gc_par_phases[phase]->set_or_add_thread_work_item(worker_i, count, index);
 }
 
+size_t G1GCPhaseTimes::get_thread_work_item(GCParPhases phase, uint worker_i, uint index) {
+  return _gc_par_phases[phase]->get_thread_work_item(worker_i, index);
+}
+
 // return the average time for a phase in milliseconds
 double G1GCPhaseTimes::average_time_ms(GCParPhases phase) {
   return _gc_par_phases[phase]->average() * 1000.0;
@@ -374,12 +394,14 @@
     info_time("Evacuate Optional Collection Set", sum_ms);
     debug_phase(_gc_par_phases[OptScanRS]);
     debug_phase(_gc_par_phases[OptObjCopy]);
+    debug_phase(_gc_par_phases[OptCodeRoots]);
+    debug_phase(_gc_par_phases[OptTermination]);
   }
   return sum_ms;
 }
 
 double G1GCPhaseTimes::print_evacuate_collection_set() const {
-  const double sum_ms = _cur_collection_par_time_ms;
+  const double sum_ms = _cur_collection_initial_evac_time_ms;
 
   info_time("Evacuate Collection Set", sum_ms);
 
@@ -517,9 +539,11 @@
       "ScanRS",
       "OptScanRS",
       "CodeRoots",
+      "OptCodeRoots",
       "ObjCopy",
       "OptObjCopy",
       "Termination",
+      "OptTermination",
       "Other",
       "GCWorkerTotal",
       "GCWorkerEnd",
--- a/src/hotspot/share/gc/g1/g1GCPhaseTimes.hpp	Mon Apr 08 11:11:22 2019 -0700
+++ b/src/hotspot/share/gc/g1/g1GCPhaseTimes.hpp	Mon Apr 08 20:37:52 2019 +0200
@@ -67,9 +67,11 @@
     ScanRS,
     OptScanRS,
     CodeRoots,
+    OptCodeRoots,
     ObjCopy,
     OptObjCopy,
     Termination,
+    OptTermination,
     Other,
     GCWorkerTotal,
     GCWorkerEnd,
@@ -87,7 +89,9 @@
   enum GCScanRSWorkItems {
     ScanRSScannedCards,
     ScanRSClaimedCards,
-    ScanRSSkippedCards
+    ScanRSSkippedCards,
+    ScanRSScannedOptRefs,
+    ScanRSUsedMemory
   };
 
   enum GCUpdateRSWorkItems {
@@ -101,13 +105,6 @@
     ObjCopyLABUndoWaste
   };
 
-  enum GCOptCSetWorkItems {
-      OptCSetScannedCards,
-      OptCSetClaimedCards,
-      OptCSetSkippedCards,
-      OptCSetUsedMemory
-  };
-
  private:
   // Markers for grouping the phases in the GCPhases enum above
   static const int GCMainParPhasesLast = GCWorkerEnd;
@@ -122,19 +119,25 @@
   WorkerDataArray<size_t>* _scan_rs_claimed_cards;
   WorkerDataArray<size_t>* _scan_rs_skipped_cards;
 
+  WorkerDataArray<size_t>* _opt_scan_rs_scanned_cards;
+  WorkerDataArray<size_t>* _opt_scan_rs_claimed_cards;
+  WorkerDataArray<size_t>* _opt_scan_rs_skipped_cards;
+  WorkerDataArray<size_t>* _opt_scan_rs_scanned_opt_refs;
+  WorkerDataArray<size_t>* _opt_scan_rs_used_memory;
+
   WorkerDataArray<size_t>* _obj_copy_lab_waste;
   WorkerDataArray<size_t>* _obj_copy_lab_undo_waste;
 
-  WorkerDataArray<size_t>* _opt_cset_scanned_cards;
-  WorkerDataArray<size_t>* _opt_cset_claimed_cards;
-  WorkerDataArray<size_t>* _opt_cset_skipped_cards;
-  WorkerDataArray<size_t>* _opt_cset_used_memory;
+  WorkerDataArray<size_t>* _opt_obj_copy_lab_waste;
+  WorkerDataArray<size_t>* _opt_obj_copy_lab_undo_waste;
 
   WorkerDataArray<size_t>* _termination_attempts;
 
+  WorkerDataArray<size_t>* _opt_termination_attempts;
+
   WorkerDataArray<size_t>* _redirtied_cards;
 
-  double _cur_collection_par_time_ms;
+  double _cur_collection_initial_evac_time_ms;
   double _cur_optional_evac_ms;
   double _cur_collection_code_root_fixup_time_ms;
   double _cur_strong_code_root_purge_time_ms;
@@ -225,10 +228,14 @@
 
   void record_or_add_time_secs(GCParPhases phase, uint worker_i, double secs);
 
+  double get_time_secs(GCParPhases phase, uint worker_i);
+
   void record_thread_work_item(GCParPhases phase, uint worker_i, size_t count, uint index = 0);
 
   void record_or_add_thread_work_item(GCParPhases phase, uint worker_i, size_t count, uint index = 0);
 
+  size_t get_thread_work_item(GCParPhases phase, uint worker_i, uint index = 0);
+
   // return the average time for a phase in milliseconds
   double average_time_ms(GCParPhases phase);
 
@@ -256,16 +263,16 @@
     _cur_expand_heap_time_ms = ms;
   }
 
-  void record_par_time(double ms) {
-    _cur_collection_par_time_ms = ms;
+  void record_initial_evac_time(double ms) {
+    _cur_collection_initial_evac_time_ms = ms;
   }
 
-  void record_optional_evacuation(double ms) {
-    _cur_optional_evac_ms = ms;
+  void record_or_add_optional_evac_time(double ms) {
+    _cur_optional_evac_ms += ms;
   }
 
-  void record_code_root_fixup_time(double ms) {
-    _cur_collection_code_root_fixup_time_ms = ms;
+  void record_or_add_code_root_fixup_time(double ms) {
+    _cur_collection_code_root_fixup_time_ms += ms;
   }
 
   void record_strong_code_root_purge_time(double ms) {
@@ -360,7 +367,7 @@
   }
 
   double cur_collection_par_time_ms() {
-    return _cur_collection_par_time_ms;
+    return _cur_collection_initial_evac_time_ms;
   }
 
   double cur_clear_ct_time_ms() {
--- a/src/hotspot/share/gc/g1/g1HeapVerifier.cpp	Mon Apr 08 11:11:22 2019 -0700
+++ b/src/hotspot/share/gc/g1/g1HeapVerifier.cpp	Mon Apr 08 20:37:52 2019 +0200
@@ -371,6 +371,7 @@
   }
 
   bool do_heap_region(HeapRegion* r) {
+    guarantee(!r->has_index_in_opt_cset(), "Region %u still has opt collection set index %u", r->hrm_index(), r->index_in_opt_cset());
     guarantee(!r->is_young() || r->rem_set()->is_complete(), "Remembered set for Young region %u must be complete, is %s", r->hrm_index(), r->rem_set()->get_state_str());
     // Humongous and old regions regions might be of any state, so can't check here.
     guarantee(!r->is_free() || !r->rem_set()->is_tracked(), "Remembered set for free region %u must be untracked, is %s", r->hrm_index(), r->rem_set()->get_state_str());
--- a/src/hotspot/share/gc/g1/g1OopStarChunkedList.cpp	Mon Apr 08 11:11:22 2019 -0700
+++ b/src/hotspot/share/gc/g1/g1OopStarChunkedList.cpp	Mon Apr 08 20:37:52 2019 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 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
@@ -32,9 +32,11 @@
   delete_list(_coops);
 }
 
-void G1OopStarChunkedList::oops_do(OopClosure* obj_cl, OopClosure* root_cl) {
-  chunks_do(_roots, root_cl);
-  chunks_do(_croots, root_cl);
-  chunks_do(_oops, obj_cl);
-  chunks_do(_coops, obj_cl);
+size_t G1OopStarChunkedList::oops_do(OopClosure* obj_cl, OopClosure* root_cl) {
+  size_t result = 0;
+  result += chunks_do(_roots, root_cl);
+  result += chunks_do(_croots, root_cl);
+  result += chunks_do(_oops, obj_cl);
+  result += chunks_do(_coops, obj_cl);
+  return result;
 }
--- a/src/hotspot/share/gc/g1/g1OopStarChunkedList.hpp	Mon Apr 08 11:11:22 2019 -0700
+++ b/src/hotspot/share/gc/g1/g1OopStarChunkedList.hpp	Mon Apr 08 20:37:52 2019 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, 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
@@ -41,8 +41,8 @@
   template <typename T> void delete_list(ChunkedList<T*, mtGC>* c);
 
   template <typename T>
-  void chunks_do(ChunkedList<T*, mtGC>* head,
-                 OopClosure* cl);
+  size_t chunks_do(ChunkedList<T*, mtGC>* head,
+                   OopClosure* cl);
 
   template <typename T>
   inline void push(ChunkedList<T*, mtGC>** field, T* p);
@@ -53,7 +53,7 @@
 
   size_t used_memory() { return _used_memory; }
 
-  void oops_do(OopClosure* obj_cl, OopClosure* root_cl);
+  size_t oops_do(OopClosure* obj_cl, OopClosure* root_cl);
 
   inline void push_oop(oop* p);
   inline void push_oop(narrowOop* p);
--- a/src/hotspot/share/gc/g1/g1OopStarChunkedList.inline.hpp	Mon Apr 08 11:11:22 2019 -0700
+++ b/src/hotspot/share/gc/g1/g1OopStarChunkedList.inline.hpp	Mon Apr 08 20:37:52 2019 +0200
@@ -72,13 +72,16 @@
 }
 
 template <typename T>
-void G1OopStarChunkedList::chunks_do(ChunkedList<T*, mtGC>* head, OopClosure* cl) {
+size_t G1OopStarChunkedList::chunks_do(ChunkedList<T*, mtGC>* head, OopClosure* cl) {
+  size_t result = 0;
   for (ChunkedList<T*, mtGC>* c = head; c != NULL; c = c->next_used()) {
+    result += c->size();
     for (size_t i = 0; i < c->size(); i++) {
       T* p = c->at(i);
       cl->do_oop(p);
     }
   }
+  return result;
 }
 
 #endif // SHARE_GC_G1_G1OOPSTARCHUNKEDLIST_INLINE_HPP
--- a/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp	Mon Apr 08 11:11:22 2019 -0700
+++ b/src/hotspot/share/gc/g1/g1ParScanThreadState.cpp	Mon Apr 08 20:37:52 2019 +0200
@@ -372,7 +372,7 @@
     }
 
     size_t used_memory = pss->oops_into_optional_region(hr)->used_memory();
-    _g1h->phase_times()->record_or_add_thread_work_item(G1GCPhaseTimes::OptScanRS, worker_index, used_memory, G1GCPhaseTimes::OptCSetUsedMemory);
+    _g1h->phase_times()->record_or_add_thread_work_item(G1GCPhaseTimes::OptScanRS, worker_index, used_memory, G1GCPhaseTimes::ScanRSUsedMemory);
   }
 }
 
--- a/src/hotspot/share/gc/g1/g1Policy.cpp	Mon Apr 08 11:11:22 2019 -0700
+++ b/src/hotspot/share/gc/g1/g1Policy.cpp	Mon Apr 08 20:37:52 2019 +0200
@@ -659,7 +659,11 @@
 
     double cost_per_entry_ms = 0.0;
     if (cards_scanned > 10) {
-      cost_per_entry_ms = average_time_ms(G1GCPhaseTimes::ScanRS) / (double) cards_scanned;
+      double avg_time_scan_rs = average_time_ms(G1GCPhaseTimes::ScanRS);
+      if (this_pause_was_young_only) {
+        avg_time_scan_rs += average_time_ms(G1GCPhaseTimes::OptScanRS);
+      }
+      cost_per_entry_ms = avg_time_scan_rs / cards_scanned;
       _analytics->report_cost_per_entry_ms(cost_per_entry_ms, this_pause_was_young_only);
     }
 
@@ -694,7 +698,7 @@
     double cost_per_byte_ms = 0.0;
 
     if (copied_bytes > 0) {
-      cost_per_byte_ms = average_time_ms(G1GCPhaseTimes::ObjCopy) / (double) copied_bytes;
+      cost_per_byte_ms = (average_time_ms(G1GCPhaseTimes::ObjCopy) + average_time_ms(G1GCPhaseTimes::OptObjCopy)) / (double) copied_bytes;
       _analytics->report_cost_per_byte_ms(cost_per_byte_ms, collector_state()->mark_or_rebuild_in_progress());
     }
 
@@ -1188,11 +1192,135 @@
   return (uint) result;
 }
 
-uint G1Policy::finalize_collection_set(double target_pause_time_ms, G1SurvivorRegions* survivor) {
-  double time_remaining_ms = _collection_set->finalize_young_part(target_pause_time_ms, survivor);
-  _collection_set->finalize_old_part(time_remaining_ms);
+void G1Policy::calculate_old_collection_set_regions(G1CollectionSetCandidates* candidates,
+                                                    double time_remaining_ms,
+                                                    uint& num_initial_regions,
+                                                    uint& num_optional_regions) {
+  assert(candidates != NULL, "Must be");
 
-  return _collection_set->region_length();
+  num_initial_regions = 0;
+  num_optional_regions = 0;
+  uint num_expensive_regions = 0;
+
+  double predicted_old_time_ms = 0.0;
+  double predicted_initial_time_ms = 0.0;
+  double predicted_optional_time_ms = 0.0;
+
+  double optional_threshold_ms = time_remaining_ms * optional_prediction_fraction();
+
+  const uint min_old_cset_length = calc_min_old_cset_length();
+  const uint max_old_cset_length = MAX2(min_old_cset_length, calc_max_old_cset_length());
+  const uint max_optional_regions = max_old_cset_length - min_old_cset_length;
+  bool check_time_remaining = adaptive_young_list_length();
+
+  uint candidate_idx = candidates->cur_idx();
+
+  log_debug(gc, ergo, cset)("Start adding old regions to collection set. Min %u regions, max %u regions, "
+                            "time remaining %1.2fms, optional threshold %1.2fms",
+                            min_old_cset_length, max_old_cset_length, time_remaining_ms, optional_threshold_ms);
+
+  HeapRegion* hr = candidates->at(candidate_idx);
+  while (hr != NULL) {
+    if (num_initial_regions + num_optional_regions >= max_old_cset_length) {
+      // Added maximum number of old regions to the CSet.
+      log_debug(gc, ergo, cset)("Finish adding old regions to collection set (Maximum number of regions). "
+                                "Initial %u regions, optional %u regions",
+                                num_initial_regions, num_optional_regions);
+      break;
+    }
+
+    // Stop adding regions if the remaining reclaimable space is
+    // not above G1HeapWastePercent.
+    size_t reclaimable_bytes = candidates->remaining_reclaimable_bytes();
+    double reclaimable_percent = reclaimable_bytes_percent(reclaimable_bytes);
+    double threshold = (double) G1HeapWastePercent;
+    if (reclaimable_percent <= threshold) {
+      // We've added enough old regions that the amount of uncollected
+      // reclaimable space is at or below the waste threshold. Stop
+      // adding old regions to the CSet.
+      log_debug(gc, ergo, cset)("Finish adding old regions to collection set (Reclaimable percentage below threshold). "
+                                "Reclaimable: " SIZE_FORMAT "%s (%1.2f%%) threshold: " UINTX_FORMAT "%%",
+                                byte_size_in_proper_unit(reclaimable_bytes), proper_unit_for_byte_size(reclaimable_bytes),
+                                reclaimable_percent, G1HeapWastePercent);
+      break;
+    }
+
+    double predicted_time_ms = predict_region_elapsed_time_ms(hr, false);
+    time_remaining_ms = MAX2(time_remaining_ms - predicted_time_ms, 0.0);
+    // Add regions to old set until we reach the minimum amount
+    if (num_initial_regions < min_old_cset_length) {
+      predicted_old_time_ms += predicted_time_ms;
+      num_initial_regions++;
+      // Record the number of regions added with no time remaining
+      if (time_remaining_ms == 0.0) {
+        num_expensive_regions++;
+      }
+    } else if (!check_time_remaining) {
+      // In the non-auto-tuning case, we'll finish adding regions
+      // to the CSet if we reach the minimum.
+      log_debug(gc, ergo, cset)("Finish adding old regions to collection set (Region amount reached min).");
+      break;
+    } else {
+      // Keep adding regions to old set until we reach the optional threshold
+      if (time_remaining_ms > optional_threshold_ms) {
+        predicted_old_time_ms += predicted_time_ms;
+        num_initial_regions++;
+      } else if (time_remaining_ms > 0) {
+        // Keep adding optional regions until time is up.
+        assert(num_optional_regions < max_optional_regions, "Should not be possible.");
+        predicted_optional_time_ms += predicted_time_ms;
+        num_optional_regions++;
+      } else {
+        log_debug(gc, ergo, cset)("Finish adding old regions to collection set (Predicted time too high).");
+        break;
+      }
+    }
+    hr = candidates->at(++candidate_idx);
+  }
+  if (hr == NULL) {
+    log_debug(gc, ergo, cset)("Old candidate collection set empty.");
+  }
+
+  if (num_expensive_regions > 0) {
+    log_debug(gc, ergo, cset)("Added %u initial old regions to collection set although the predicted time was too high.",
+                              num_expensive_regions);
+  }
+
+  log_debug(gc, ergo, cset)("Finish choosing collection set old regions. Initial: %u, optional: %u, "
+                            "predicted old time: %1.2fms, predicted optional time: %1.2fms, time remaining: %1.2f",
+                            num_initial_regions, num_optional_regions,
+                            predicted_initial_time_ms, predicted_optional_time_ms, time_remaining_ms);
+}
+
+void G1Policy::calculate_optional_collection_set_regions(G1CollectionSetCandidates* candidates,
+                                                         uint const max_optional_regions,
+                                                         double time_remaining_ms,
+                                                         uint& num_optional_regions) {
+  assert(_g1h->collector_state()->in_mixed_phase(), "Should only be called in mixed phase");
+
+  num_optional_regions = 0;
+  double prediction_ms = 0;
+  uint candidate_idx = candidates->cur_idx();
+
+  HeapRegion* r = candidates->at(candidate_idx);
+  while (num_optional_regions < max_optional_regions) {
+    assert(r != NULL, "Region must exist");
+    prediction_ms += predict_region_elapsed_time_ms(r, false);
+
+    if (prediction_ms > time_remaining_ms) {
+      log_debug(gc, ergo, cset)("Prediction %.3fms for region %u does not fit remaining time: %.3fms.",
+                                prediction_ms, r->hrm_index(), time_remaining_ms);
+      break;
+    }
+    // This region will be included in the next optional evacuation.
+
+    time_remaining_ms -= prediction_ms;
+    num_optional_regions++;
+    r = candidates->at(++candidate_idx);
+  }
+
+  log_debug(gc, ergo, cset)("Prepared %u regions out of %u for optional evacuation. Predicted time: %.3fms",
+                            num_optional_regions, max_optional_regions, prediction_ms);
 }
 
 void G1Policy::transfer_survivors_to_cset(const G1SurvivorRegions* survivors) {
--- a/src/hotspot/share/gc/g1/g1Policy.hpp	Mon Apr 08 11:11:22 2019 -0700
+++ b/src/hotspot/share/gc/g1/g1Policy.hpp	Mon Apr 08 20:37:52 2019 +0200
@@ -44,6 +44,7 @@
 
 class HeapRegion;
 class G1CollectionSet;
+class G1CollectionSetCandidates;
 class G1CollectionSetChooser;
 class G1IHOPControl;
 class G1Analytics;
@@ -344,7 +345,21 @@
   bool next_gc_should_be_mixed(const char* true_action_str,
                                const char* false_action_str) const;
 
-  uint finalize_collection_set(double target_pause_time_ms, G1SurvivorRegions* survivor);
+  // Calculate and return the number of initial and optional old gen regions from
+  // the given collection set candidates and the remaining time.
+  void calculate_old_collection_set_regions(G1CollectionSetCandidates* candidates,
+                                            double time_remaining_ms,
+                                            uint& num_initial_regions,
+                                            uint& num_optional_regions);
+
+  // Calculate the number of optional regions from the given collection set candidates,
+  // the remaining time and the maximum number of these regions and return the number
+  // of actually selected regions in num_optional_regions.
+  void calculate_optional_collection_set_regions(G1CollectionSetCandidates* candidates,
+                                                 uint const max_optional_regions,
+                                                 double time_remaining_ms,
+                                                 uint& num_optional_regions);
+
 private:
   // Set the state to start a concurrent marking cycle and clear
   // _initiate_conc_mark_if_possible because it has now been
@@ -403,11 +418,13 @@
   AgeTable _survivors_age_table;
 
   size_t desired_survivor_size(uint max_regions) const;
-public:
+
   // Fraction used when predicting how many optional regions to include in
   // the CSet. This fraction of the available time is used for optional regions,
   // the rest is used to add old regions to the normal CSet.
   double optional_prediction_fraction() { return 0.2; }
+
+public:
   // Fraction used when evacuating the optional regions. This fraction of the
   // remaining time is used to choose what regions to include in the evacuation.
   double optional_evacuation_fraction() { return 0.75; }
--- a/src/hotspot/share/gc/g1/g1RemSet.cpp	Mon Apr 08 11:11:22 2019 -0700
+++ b/src/hotspot/share/gc/g1/g1RemSet.cpp	Mon Apr 08 20:37:52 2019 +0200
@@ -316,6 +316,8 @@
   _scan_state(scan_state),
   _phase(phase),
   _worker_i(worker_i),
+  _opt_refs_scanned(0),
+  _opt_refs_memory_used(0),
   _cards_scanned(0),
   _cards_claimed(0),
   _cards_skipped(0),
@@ -338,6 +340,19 @@
   _cards_scanned++;
 }
 
+void G1ScanRSForRegionClosure::scan_opt_rem_set_roots(HeapRegion* r) {
+  EventGCPhaseParallel event;
+
+  G1OopStarChunkedList* opt_rem_set_list = _pss->oops_into_optional_region(r);
+
+  G1ScanObjsDuringScanRSClosure 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();
+
+  event.commit(GCId::current(), _worker_i, G1GCPhaseTimes::phase_name(_phase));
+}
+
 void G1ScanRSForRegionClosure::scan_rem_set_roots(HeapRegion* r) {
   EventGCPhaseParallel event;
   uint const region_idx = r->hrm_index();
@@ -414,11 +429,16 @@
 }
 
 bool G1ScanRSForRegionClosure::do_heap_region(HeapRegion* r) {
-  assert(r->in_collection_set(),
-         "Should only be called on elements of the collection set but region %u is not.",
-         r->hrm_index());
+  assert(r->in_collection_set(), "Region %u is not in the collection set.", r->hrm_index());
   uint const region_idx = r->hrm_index();
 
+  // The individual references for the optional remembered set are per-worker, so we
+  // always need to scan them.
+  if (r->has_index_in_opt_cset()) {
+    G1EvacPhaseWithTrimTimeTracker timer(_pss, _rem_set_root_scan_time, _rem_set_trim_partially_time);
+    scan_opt_rem_set_roots(r);
+  }
+
   // Do an early out if we know we are complete.
   if (_scan_state->iter_is_complete(region_idx)) {
     return false;
@@ -437,22 +457,33 @@
   return false;
 }
 
-void G1RemSet::scan_rem_set(G1ParScanThreadState* pss, uint worker_i) {
+void G1RemSet::scan_rem_set(G1ParScanThreadState* pss,
+                            uint worker_i,
+                            G1GCPhaseTimes::GCParPhases scan_phase,
+                            G1GCPhaseTimes::GCParPhases objcopy_phase,
+                            G1GCPhaseTimes::GCParPhases coderoots_phase) {
+  assert(pss->trim_ticks().value() == 0, "Queues must have been trimmed before entering.");
+
   G1ScanObjsDuringScanRSClosure scan_cl(_g1h, pss);
-  G1ScanRSForRegionClosure cl(_scan_state, &scan_cl, pss, G1GCPhaseTimes::ScanRS, worker_i);
-  _g1h->collection_set_iterate_from(&cl, worker_i);
+  G1ScanRSForRegionClosure cl(_scan_state, &scan_cl, pss, scan_phase, worker_i);
+  _g1h->collection_set_iterate_increment_from(&cl, worker_i);
 
   G1GCPhaseTimes* p = _g1p->phase_times();
 
-  p->record_time_secs(G1GCPhaseTimes::ScanRS, worker_i, cl.rem_set_root_scan_time().seconds());
-  p->add_time_secs(G1GCPhaseTimes::ObjCopy, worker_i, cl.rem_set_trim_partially_time().seconds());
+  p->record_or_add_time_secs(objcopy_phase, worker_i, cl.rem_set_trim_partially_time().seconds());
 
-  p->record_thread_work_item(G1GCPhaseTimes::ScanRS, worker_i, cl.cards_scanned(), G1GCPhaseTimes::ScanRSScannedCards);
-  p->record_thread_work_item(G1GCPhaseTimes::ScanRS, worker_i, cl.cards_claimed(), G1GCPhaseTimes::ScanRSClaimedCards);
-  p->record_thread_work_item(G1GCPhaseTimes::ScanRS, worker_i, cl.cards_skipped(), G1GCPhaseTimes::ScanRSSkippedCards);
+  p->record_or_add_time_secs(scan_phase, worker_i, cl.rem_set_root_scan_time().seconds());
+  p->record_or_add_thread_work_item(scan_phase, worker_i, cl.cards_scanned(), G1GCPhaseTimes::ScanRSScannedCards);
+  p->record_or_add_thread_work_item(scan_phase, worker_i, cl.cards_claimed(), G1GCPhaseTimes::ScanRSClaimedCards);
+  p->record_or_add_thread_work_item(scan_phase, worker_i, cl.cards_skipped(), G1GCPhaseTimes::ScanRSSkippedCards);
+  // At this time we only record some metrics for the optional remembered set.
+  if (scan_phase == G1GCPhaseTimes::OptScanRS) {
+    p->record_or_add_thread_work_item(scan_phase, worker_i, cl.opt_refs_scanned(), G1GCPhaseTimes::ScanRSScannedOptRefs);
+    p->record_or_add_thread_work_item(scan_phase, worker_i, cl.opt_refs_memory_used(), G1GCPhaseTimes::ScanRSUsedMemory);
+  }
 
-  p->record_time_secs(G1GCPhaseTimes::CodeRoots, worker_i, cl.strong_code_root_scan_time().seconds());
-  p->add_time_secs(G1GCPhaseTimes::ObjCopy, worker_i, cl.strong_code_root_trim_partially_time().seconds());
+  p->record_or_add_time_secs(coderoots_phase, worker_i, cl.strong_code_root_scan_time().seconds());
+  p->add_time_secs(objcopy_phase, worker_i, cl.strong_code_root_trim_partially_time().seconds());
 }
 
 // Closure used for updating rem sets. Only called during an evacuation pause.
@@ -514,11 +545,6 @@
   }
 }
 
-void G1RemSet::oops_into_collection_set_do(G1ParScanThreadState* pss, uint worker_i) {
-  update_rem_set(pss, worker_i);
-  scan_rem_set(pss, worker_i);;
-}
-
 void G1RemSet::prepare_for_oops_into_collection_set_do() {
   G1BarrierSet::dirty_card_queue_set().concatenate_logs();
   _scan_state->reset();
--- a/src/hotspot/share/gc/g1/g1RemSet.hpp	Mon Apr 08 11:11:22 2019 -0700
+++ b/src/hotspot/share/gc/g1/g1RemSet.hpp	Mon Apr 08 20:37:52 2019 +0200
@@ -60,14 +60,6 @@
 
   G1RemSetSummary _prev_period_summary;
 
-  // Scan all remembered sets of the collection set for references into the collection
-  // set.
-  void scan_rem_set(G1ParScanThreadState* pss, uint worker_i);
-
-  // Flush remaining refinement buffers for cross-region references to either evacuate references
-  // into the collection set or update the remembered set.
-  void update_rem_set(G1ParScanThreadState* pss, uint worker_i);
-
   G1CollectedHeap* _g1h;
   size_t _num_conc_refined_cards; // Number of cards refined concurrently to the mutator.
 
@@ -93,12 +85,19 @@
            G1HotCardCache* hot_card_cache);
   ~G1RemSet();
 
-  // Process all oops in the collection set from the cards in the refinement buffers and
-  // remembered sets using pss.
-  //
+  // Scan all remembered sets of the collection set for references into the collection
+  // set.
   // Further applies heap_region_codeblobs on the oops of the unmarked nmethods on the strong code
   // roots list for each region in the collection set.
-  void oops_into_collection_set_do(G1ParScanThreadState* pss, uint worker_i);
+  void scan_rem_set(G1ParScanThreadState* pss,
+                    uint worker_i,
+                    G1GCPhaseTimes::GCParPhases scan_phase,
+                    G1GCPhaseTimes::GCParPhases objcopy_phase,
+                    G1GCPhaseTimes::GCParPhases coderoots_phase);
+
+  // Flush remaining refinement buffers for cross-region references to either evacuate references
+  // into the collection set or update the remembered set.
+  void update_rem_set(G1ParScanThreadState* pss, uint worker_i);
 
   // Prepare for and cleanup after an oops_into_collection_set_do
   // call.  Must call each of these once before and after (in sequential
@@ -144,6 +143,9 @@
 
   uint   _worker_i;
 
+  size_t _opt_refs_scanned;
+  size_t _opt_refs_memory_used;
+
   size_t _cards_scanned;
   size_t _cards_claimed;
   size_t _cards_skipped;
@@ -157,6 +159,7 @@
   void claim_card(size_t card_index, const uint region_idx_for_card);
   void scan_card(MemRegion mr, uint region_idx_for_card);
 
+  void scan_opt_rem_set_roots(HeapRegion* r);
   void scan_rem_set_roots(HeapRegion* r);
   void scan_strong_code_roots(HeapRegion* r);
 public:
@@ -177,6 +180,9 @@
   size_t cards_scanned() const { return _cards_scanned; }
   size_t cards_claimed() const { return _cards_claimed; }
   size_t cards_skipped() const { return _cards_skipped; }
+
+  size_t opt_refs_scanned() const { return _opt_refs_scanned; }
+  size_t opt_refs_memory_used() const { return _opt_refs_memory_used; }
 };
 
 #endif // SHARE_GC_G1_G1REMSET_HPP
--- a/src/hotspot/share/gc/g1/heapRegion.cpp	Mon Apr 08 11:11:22 2019 -0700
+++ b/src/hotspot/share/gc/g1/heapRegion.cpp	Mon Apr 08 20:37:52 2019 +0200
@@ -117,6 +117,7 @@
          "Should not clear heap region %u in the collection set", hrm_index());
 
   set_young_index_in_cset(-1);
+  clear_index_in_opt_cset();
   uninstall_surv_rate_group();
   set_free();
   reset_pre_dummy_top();
@@ -241,7 +242,7 @@
     _containing_set(NULL),
 #endif
     _prev_marked_bytes(0), _next_marked_bytes(0), _gc_efficiency(0.0),
-    _index_in_opt_cset(G1OptionalCSet::InvalidCSetIndex), _young_index_in_cset(-1),
+    _index_in_opt_cset(InvalidCSetIndex), _young_index_in_cset(-1),
     _surv_rate_group(NULL), _age_index(-1),
     _prev_top_at_mark_start(NULL), _next_top_at_mark_start(NULL),
     _recorded_rs_length(0), _predicted_elapsed_time_ms(0)
--- a/src/hotspot/share/gc/g1/heapRegion.hpp	Mon Apr 08 11:11:22 2019 -0700
+++ b/src/hotspot/share/gc/g1/heapRegion.hpp	Mon Apr 08 20:37:52 2019 +0200
@@ -250,6 +250,8 @@
   // The calculated GC efficiency of the region.
   double _gc_efficiency;
 
+  static const uint InvalidCSetIndex = UINT_MAX;
+
   // The index in the optional regions array, if this region
   // is considered optional during a mixed collections.
   uint _index_in_opt_cset;
@@ -549,8 +551,13 @@
   void calc_gc_efficiency(void);
   double gc_efficiency() const { return _gc_efficiency;}
 
-  uint index_in_opt_cset() const { return _index_in_opt_cset; }
+  uint index_in_opt_cset() const {
+    assert(has_index_in_opt_cset(), "Opt cset index not set.");
+    return _index_in_opt_cset;
+  }
+  bool has_index_in_opt_cset() const { return _index_in_opt_cset != InvalidCSetIndex; }
   void set_index_in_opt_cset(uint index) { _index_in_opt_cset = index; }
+  void clear_index_in_opt_cset() { _index_in_opt_cset = InvalidCSetIndex; }
 
   int  young_index_in_cset() const { return _young_index_in_cset; }
   void set_young_index_in_cset(int index) {
--- a/src/hotspot/share/gc/shared/workerDataArray.hpp	Mon Apr 08 11:11:22 2019 -0700
+++ b/src/hotspot/share/gc/shared/workerDataArray.hpp	Mon Apr 08 20:37:52 2019 +0200
@@ -34,7 +34,7 @@
 class WorkerDataArray  : public CHeapObj<mtGC> {
   friend class WDAPrinter;
 public:
-  static const uint MaxThreadWorkItems = 4;
+  static const uint MaxThreadWorkItems = 5;
 private:
   T*          _data;
   uint        _length;
@@ -50,6 +50,7 @@
   void set_thread_work_item(uint worker_i, size_t value, uint index = 0);
   void add_thread_work_item(uint worker_i, size_t value, uint index = 0);
   void set_or_add_thread_work_item(uint worker_i, size_t value, uint index = 0);
+  size_t get_thread_work_item(uint worker_i, uint index = 0);
 
   WorkerDataArray<size_t>* thread_work_items(uint index = 0) const {
     assert(index < MaxThreadWorkItems, "Tried to access thread work item %u max %u", index, MaxThreadWorkItems);
--- a/src/hotspot/share/gc/shared/workerDataArray.inline.hpp	Mon Apr 08 11:11:22 2019 -0700
+++ b/src/hotspot/share/gc/shared/workerDataArray.inline.hpp	Mon Apr 08 20:37:52 2019 +0200
@@ -92,6 +92,13 @@
 }
 
 template <typename T>
+size_t WorkerDataArray<T>::get_thread_work_item(uint worker_i, uint index) {
+  assert(index < MaxThreadWorkItems, "Tried to access thread work item %u (max %u)", index, MaxThreadWorkItems);
+  assert(_thread_work_items[index] != NULL, "No sub count");
+  return _thread_work_items[index]->get(worker_i);
+}
+
+template <typename T>
 void WorkerDataArray<T>::add(uint worker_i, T value) {
   assert(worker_i < _length, "Worker %d is greater than max: %d", worker_i, _length);
   assert(_data[worker_i] != uninitialized(), "No data to add to for worker %d", worker_i);