8004687: G1: Parallelize object self-forwarding and scanning during an evacuation failure
authortschatzl
Thu, 23 Jul 2015 11:14:47 +0200
changeset 31976 da45f85bf4e1
parent 31975 1e8d389159cb
child 31977 a737c35bf8f6
child 31979 fb7be4e8a187
8004687: G1: Parallelize object self-forwarding and scanning during an evacuation failure Summary: Use the regular task queue during evacuation failure and allow per-thread preserved header queues to remove the global lock during evacuation failure. Reviewed-by: mgerdin, jmasa Contributed-by: Walter Florian Gugenberger <walter.gugenberger@gmail.com>
hotspot/src/share/vm/gc/g1/g1CollectedHeap.cpp
hotspot/src/share/vm/gc/g1/g1CollectedHeap.hpp
hotspot/src/share/vm/gc/g1/g1OopClosures.hpp
hotspot/src/share/vm/gc/g1/g1ParScanThreadState.cpp
hotspot/src/share/vm/gc/g1/g1ParScanThreadState.hpp
hotspot/src/share/vm/runtime/mutexLocker.cpp
hotspot/src/share/vm/runtime/mutexLocker.hpp
--- a/hotspot/src/share/vm/gc/g1/g1CollectedHeap.cpp	Thu Jul 23 11:14:24 2015 +0200
+++ b/hotspot/src/share/vm/gc/g1/g1CollectedHeap.cpp	Thu Jul 23 11:14:47 2015 +0200
@@ -1917,7 +1917,6 @@
   _ref_processor_cm(NULL),
   _ref_processor_stw(NULL),
   _bot_shared(NULL),
-  _evac_failure_scan_stack(NULL),
   _cg1r(NULL),
   _g1mm(NULL),
   _refine_cte_cl(NULL),
@@ -2205,6 +2204,11 @@
 
   G1StringDedup::initialize();
 
+  _preserved_objs = NEW_C_HEAP_ARRAY(OopAndMarkOopStack, ParallelGCThreads, mtGC);
+  for (uint i = 0; i < ParallelGCThreads; i++) {
+    new (&_preserved_objs[i]) OopAndMarkOopStack();
+  }
+
   return JNI_OK;
 }
 
@@ -4256,21 +4260,6 @@
   return true;
 }
 
-void G1CollectedHeap::init_for_evac_failure(OopsInHeapRegionClosure* cl) {
-  _drain_in_progress = false;
-  set_evac_failure_closure(cl);
-  _evac_failure_scan_stack = new (ResourceObj::C_HEAP, mtGC) GrowableArray<oop>(40, true);
-}
-
-void G1CollectedHeap::finalize_for_evac_failure() {
-  assert(_evac_failure_scan_stack != NULL &&
-         _evac_failure_scan_stack->length() == 0,
-         "Postcondition");
-  assert(!_drain_in_progress, "Postcondition");
-  delete _evac_failure_scan_stack;
-  _evac_failure_scan_stack = NULL;
-}
-
 void G1CollectedHeap::remove_self_forwarding_pointers() {
   double remove_self_forwards_start = os::elapsedTime();
 
@@ -4278,104 +4267,30 @@
   workers()->run_task(&rsfp_task);
 
   // Now restore saved marks, if any.
-  assert(_objs_with_preserved_marks.size() ==
-            _preserved_marks_of_objs.size(), "Both or none.");
-  while (!_objs_with_preserved_marks.is_empty()) {
-    oop obj = _objs_with_preserved_marks.pop();
-    markOop m = _preserved_marks_of_objs.pop();
-    obj->set_mark(m);
-  }
-  _objs_with_preserved_marks.clear(true);
-  _preserved_marks_of_objs.clear(true);
+  for (uint i = 0; i < ParallelGCThreads; i++) {
+    OopAndMarkOopStack& cur = _preserved_objs[i];
+    while (!cur.is_empty()) {
+      OopAndMarkOop elem = cur.pop();
+      elem.set_mark();
+    }
+    cur.clear(true);
+  }
 
   g1_policy()->phase_times()->record_evac_fail_remove_self_forwards((os::elapsedTime() - remove_self_forwards_start) * 1000.0);
 }
 
-void G1CollectedHeap::push_on_evac_failure_scan_stack(oop obj) {
-  _evac_failure_scan_stack->push(obj);
-}
-
-void G1CollectedHeap::drain_evac_failure_scan_stack() {
-  assert(_evac_failure_scan_stack != NULL, "precondition");
-
-  while (_evac_failure_scan_stack->length() > 0) {
-     oop obj = _evac_failure_scan_stack->pop();
-     _evac_failure_closure->set_region(heap_region_containing(obj));
-     obj->oop_iterate_backwards(_evac_failure_closure);
-  }
-}
-
-oop
-G1CollectedHeap::handle_evacuation_failure_par(G1ParScanThreadState* _par_scan_state,
-                                               oop old) {
-  assert(obj_in_cs(old),
-         err_msg("obj: " PTR_FORMAT " should still be in the CSet",
-                 p2i(old)));
-  markOop m = old->mark();
-  oop forward_ptr = old->forward_to_atomic(old);
-  if (forward_ptr == NULL) {
-    // Forward-to-self succeeded.
-    assert(_par_scan_state != NULL, "par scan state");
-    OopsInHeapRegionClosure* cl = _par_scan_state->evac_failure_closure();
-    uint queue_num = _par_scan_state->queue_num();
-
+void G1CollectedHeap::preserve_mark_during_evac_failure(uint queue_num, oop obj, markOop m) {
+  if (!_evacuation_failed) {
     _evacuation_failed = true;
-    _evacuation_failed_info_array[queue_num].register_copy_failure(old->size());
-    if (_evac_failure_closure != cl) {
-      MutexLockerEx x(EvacFailureStack_lock, Mutex::_no_safepoint_check_flag);
-      assert(!_drain_in_progress,
-             "Should only be true while someone holds the lock.");
-      // Set the global evac-failure closure to the current thread's.
-      assert(_evac_failure_closure == NULL, "Or locking has failed.");
-      set_evac_failure_closure(cl);
-      // Now do the common part.
-      handle_evacuation_failure_common(old, m);
-      // Reset to NULL.
-      set_evac_failure_closure(NULL);
-    } else {
-      // The lock is already held, and this is recursive.
-      assert(_drain_in_progress, "This should only be the recursive case.");
-      handle_evacuation_failure_common(old, m);
-    }
-    return old;
-  } else {
-    // Forward-to-self failed. Either someone else managed to allocate
-    // space for this object (old != forward_ptr) or they beat us in
-    // self-forwarding it (old == forward_ptr).
-    assert(old == forward_ptr || !obj_in_cs(forward_ptr),
-           err_msg("obj: " PTR_FORMAT " forwarded to: " PTR_FORMAT " "
-                   "should not be in the CSet",
-                   p2i(old), p2i(forward_ptr)));
-    return forward_ptr;
-  }
-}
-
-void G1CollectedHeap::handle_evacuation_failure_common(oop old, markOop m) {
-  preserve_mark_if_necessary(old, m);
-
-  HeapRegion* r = heap_region_containing(old);
-  if (!r->evacuation_failed()) {
-    r->set_evacuation_failed(true);
-    _hr_printer.evac_failure(r);
-  }
-
-  push_on_evac_failure_scan_stack(old);
-
-  if (!_drain_in_progress) {
-    // prevent recursion in copy_to_survivor_space()
-    _drain_in_progress = true;
-    drain_evac_failure_scan_stack();
-    _drain_in_progress = false;
-  }
-}
-
-void G1CollectedHeap::preserve_mark_if_necessary(oop obj, markOop m) {
-  assert(evacuation_failed(), "Oversaving!");
+  }
+
+  _evacuation_failed_info_array[queue_num].register_copy_failure(obj->size());
+
   // We want to call the "for_promotion_failure" version only in the
   // case of a promotion failure.
   if (m->must_be_preserved_for_promotion_failure(obj)) {
-    _objs_with_preserved_marks.push(obj);
-    _preserved_marks_of_objs.push(m);
+    OopAndMarkOop elem(obj, m);
+    _preserved_objs[queue_num].push(elem);
   }
 }
 
@@ -4451,14 +4366,7 @@
       mark_object(obj);
     }
   }
-
-  if (barrier == G1BarrierEvac) {
-    _par_scan_state->update_rs(_from, p, _worker_id);
-  }
-}
-
-template void G1ParCopyClosure<G1BarrierEvac, G1MarkNone>::do_oop_work(oop* p);
-template void G1ParCopyClosure<G1BarrierEvac, G1MarkNone>::do_oop_work(narrowOop* p);
+}
 
 class G1ParEvacuateFollowersClosure : public VoidClosure {
 protected:
@@ -4598,9 +4506,6 @@
       ReferenceProcessor*             rp = _g1h->ref_processor_stw();
 
       G1ParScanThreadState            pss(_g1h, worker_id, rp);
-      G1ParScanHeapEvacFailureClosure evac_failure_cl(_g1h, &pss, rp);
-
-      pss.set_evac_failure_closure(&evac_failure_cl);
 
       bool only_young = _g1h->collector_state()->gcs_are_young();
 
@@ -5270,9 +5175,6 @@
     G1STWIsAliveClosure is_alive(_g1h);
 
     G1ParScanThreadState            pss(_g1h, worker_id, NULL);
-    G1ParScanHeapEvacFailureClosure evac_failure_cl(_g1h, &pss, NULL);
-
-    pss.set_evac_failure_closure(&evac_failure_cl);
 
     G1ParScanExtRootClosure        only_copy_non_heap_cl(_g1h, &pss, NULL);
 
@@ -5369,10 +5271,6 @@
     HandleMark   hm;
 
     G1ParScanThreadState            pss(_g1h, worker_id, NULL);
-    G1ParScanHeapEvacFailureClosure evac_failure_cl(_g1h, &pss, NULL);
-
-    pss.set_evac_failure_closure(&evac_failure_cl);
-
     assert(pss.queue_is_empty(), "both queue and overflow should be empty");
 
     G1ParScanExtRootClosure        only_copy_non_heap_cl(_g1h, &pss, NULL);
@@ -5477,15 +5375,11 @@
 
   // Use only a single queue for this PSS.
   G1ParScanThreadState            pss(this, 0, NULL);
+  assert(pss.queue_is_empty(), "pre-condition");
 
   // We do not embed a reference processor in the copying/scanning
   // closures while we're actually processing the discovered
   // reference objects.
-  G1ParScanHeapEvacFailureClosure evac_failure_cl(this, &pss, NULL);
-
-  pss.set_evac_failure_closure(&evac_failure_cl);
-
-  assert(pss.queue_is_empty(), "pre-condition");
 
   G1ParScanExtRootClosure        only_copy_non_heap_cl(this, &pss, NULL);
 
@@ -5591,8 +5485,6 @@
 
   const uint n_workers = workers()->active_workers();
 
-  init_for_evac_failure(NULL);
-
   assert(dirty_card_queue_set().completed_buffers_num() == 0, "Should be empty");
   double start_par_time_sec = os::elapsedTime();
   double end_par_time_sec;
@@ -5656,8 +5548,6 @@
 
   purge_code_root_memory();
 
-  finalize_for_evac_failure();
-
   if (evacuation_failed()) {
     remove_self_forwarding_pointers();
 
--- a/hotspot/src/share/vm/gc/g1/g1CollectedHeap.hpp	Thu Jul 23 11:14:24 2015 +0200
+++ b/hotspot/src/share/vm/gc/g1/g1CollectedHeap.hpp	Thu Jul 23 11:14:47 2015 +0200
@@ -867,44 +867,27 @@
   // forwarding pointers to themselves.  Reset them.
   void remove_self_forwarding_pointers();
 
-  // Together, these store an object with a preserved mark, and its mark value.
-  Stack<oop, mtGC>     _objs_with_preserved_marks;
-  Stack<markOop, mtGC> _preserved_marks_of_objs;
+  struct OopAndMarkOop {
+   private:
+    oop _o;
+    markOop _m;
+   public:
+    OopAndMarkOop(oop obj, markOop m) : _o(obj), _m(m) {
+    }
+
+    void set_mark() {
+      _o->set_mark(_m);
+    }
+  };
+
+  typedef Stack<OopAndMarkOop,mtGC> OopAndMarkOopStack;
+  // Stores marks with the corresponding oop that we need to preserve during evacuation
+  // failure.
+  OopAndMarkOopStack*  _preserved_objs;
 
   // Preserve the mark of "obj", if necessary, in preparation for its mark
   // word being overwritten with a self-forwarding-pointer.
-  void preserve_mark_if_necessary(oop obj, markOop m);
-
-  // The stack of evac-failure objects left to be scanned.
-  GrowableArray<oop>*    _evac_failure_scan_stack;
-  // The closure to apply to evac-failure objects.
-
-  OopsInHeapRegionClosure* _evac_failure_closure;
-  // Set the field above.
-  void
-  set_evac_failure_closure(OopsInHeapRegionClosure* evac_failure_closure) {
-    _evac_failure_closure = evac_failure_closure;
-  }
-
-  // Push "obj" on the scan stack.
-  void push_on_evac_failure_scan_stack(oop obj);
-  // Process scan stack entries until the stack is empty.
-  void drain_evac_failure_scan_stack();
-  // True iff an invocation of "drain_scan_stack" is in progress; to
-  // prevent unnecessary recursion.
-  bool _drain_in_progress;
-
-  // Do any necessary initialization for evacuation-failure handling.
-  // "cl" is the closure that will be used to process evac-failure
-  // objects.
-  void init_for_evac_failure(OopsInHeapRegionClosure* cl);
-  // Do any necessary cleanup for evacuation-failure handling data
-  // structures.
-  void finalize_for_evac_failure();
-
-  // An attempt to evacuate "obj" has failed; take necessary steps.
-  oop handle_evacuation_failure_par(G1ParScanThreadState* _par_scan_state, oop obj);
-  void handle_evacuation_failure_common(oop obj, markOop m);
+  void preserve_mark_during_evac_failure(uint queue, oop obj, markOop m);
 
 #ifndef PRODUCT
   // Support for forcing evacuation failures. Analogous to
--- a/hotspot/src/share/vm/gc/g1/g1OopClosures.hpp	Thu Jul 23 11:14:24 2015 +0200
+++ b/hotspot/src/share/vm/gc/g1/g1OopClosures.hpp	Thu Jul 23 11:14:47 2015 +0200
@@ -111,7 +111,6 @@
 
 enum G1Barrier {
   G1BarrierNone,
-  G1BarrierEvac,
   G1BarrierKlass
 };
 
@@ -148,8 +147,6 @@
 // We use a separate closure to handle references during evacuation
 // failure processing.
 
-typedef G1ParCopyClosure<G1BarrierEvac, G1MarkNone> G1ParScanHeapEvacFailureClosure;
-
 class FilterIntoCSClosure: public ExtendedOopClosure {
   G1CollectedHeap* _g1;
   OopClosure* _oc;
--- a/hotspot/src/share/vm/gc/g1/g1ParScanThreadState.cpp	Thu Jul 23 11:14:24 2015 +0200
+++ b/hotspot/src/share/vm/gc/g1/g1ParScanThreadState.cpp	Thu Jul 23 11:14:47 2015 +0200
@@ -144,8 +144,6 @@
 #endif // ASSERT
 
 void G1ParScanThreadState::trim_queue() {
-  assert(_evac_failure_cl != NULL, "not set");
-
   StarTask ref;
   do {
     // Drain the overflow stack first, so other threads can steal.
@@ -222,7 +220,7 @@
       if (obj_ptr == NULL) {
         // This will either forward-to-self, or detect that someone else has
         // installed a forwarding pointer.
-        return _g1h->handle_evacuation_failure_par(this, old);
+        return handle_evacuation_failure_par(old, old_mark);
       }
     }
   }
@@ -236,7 +234,7 @@
     // Doing this after all the allocation attempts also tests the
     // undo_allocation() method too.
     _g1_par_allocator->undo_allocation(dest_state, obj_ptr, word_sz, context);
-    return _g1h->handle_evacuation_failure_par(this, old);
+    return handle_evacuation_failure_par(old, old_mark);
   }
 #endif // !PRODUCT
 
@@ -301,3 +299,36 @@
     return forward_ptr;
   }
 }
+
+oop G1ParScanThreadState::handle_evacuation_failure_par(oop old, markOop m) {
+  assert(_g1h->obj_in_cs(old),
+         err_msg("Object " PTR_FORMAT " should be in the CSet", p2i(old)));
+
+  oop forward_ptr = old->forward_to_atomic(old);
+  if (forward_ptr == NULL) {
+    // Forward-to-self succeeded. We are the "owner" of the object.
+    HeapRegion* r = _g1h->heap_region_containing(old);
+
+    if (!r->evacuation_failed()) {
+      r->set_evacuation_failed(true);
+     _g1h->hr_printer()->evac_failure(r);
+    }
+
+    _g1h->preserve_mark_during_evac_failure(_queue_num, old, m);
+
+    _scanner.set_region(r);
+    old->oop_iterate_backwards(&_scanner);
+
+    return old;
+  } else {
+    // Forward-to-self failed. Either someone else managed to allocate
+    // space for this object (old != forward_ptr) or they beat us in
+    // self-forwarding it (old == forward_ptr).
+    assert(old == forward_ptr || !_g1h->obj_in_cs(forward_ptr),
+           err_msg("Object " PTR_FORMAT " forwarded to: " PTR_FORMAT " "
+                   "should not be in the CSet",
+                   p2i(old), p2i(forward_ptr)));
+    return forward_ptr;
+  }
+}
+
--- a/hotspot/src/share/vm/gc/g1/g1ParScanThreadState.hpp	Thu Jul 23 11:14:24 2015 +0200
+++ b/hotspot/src/share/vm/gc/g1/g1ParScanThreadState.hpp	Thu Jul 23 11:14:47 2015 +0200
@@ -54,8 +54,6 @@
   uint              _tenuring_threshold;
   G1ParScanClosure  _scanner;
 
-  OopsInHeapRegionClosure*      _evac_failure_cl;
-
   int  _hash_seed;
   uint _queue_num;
 
@@ -114,12 +112,6 @@
     }
   }
 
-  void set_evac_failure_closure(OopsInHeapRegionClosure* evac_failure_cl) {
-    _evac_failure_cl = evac_failure_cl;
-  }
-
-  OopsInHeapRegionClosure* evac_failure_closure() { return _evac_failure_cl; }
-
   int* hash_seed() { return &_hash_seed; }
   uint queue_num() { return _queue_num; }
 
@@ -211,6 +203,9 @@
   void trim_queue();
 
   inline void steal_and_trim_queue(RefToScanQueueSet *task_queues);
+
+  // An attempt to evacuate "obj" has failed; take necessary steps.
+  oop handle_evacuation_failure_par(oop obj, markOop m);
 };
 
 #endif // SHARE_VM_GC_G1_G1PARSCANTHREADSTATE_HPP
--- a/hotspot/src/share/vm/runtime/mutexLocker.cpp	Thu Jul 23 11:14:24 2015 +0200
+++ b/hotspot/src/share/vm/runtime/mutexLocker.cpp	Thu Jul 23 11:14:47 2015 +0200
@@ -83,7 +83,6 @@
 Monitor* DirtyCardQ_CBL_mon           = NULL;
 Mutex*   Shared_DirtyCardQ_lock       = NULL;
 Mutex*   ParGCRareEvent_lock          = NULL;
-Mutex*   EvacFailureStack_lock        = NULL;
 Mutex*   DerivedPointerTableGC_lock   = NULL;
 Mutex*   Compile_lock                 = NULL;
 Monitor* MethodCompileQueue_lock      = NULL;
@@ -201,7 +200,6 @@
     def(OldSets_lock               , Mutex  , leaf     ,   true,  Monitor::_safepoint_check_never);
     def(RootRegionScan_lock        , Monitor, leaf     ,   true,  Monitor::_safepoint_check_never);
     def(MMUTracker_lock            , Mutex  , leaf     ,   true,  Monitor::_safepoint_check_never);
-    def(EvacFailureStack_lock      , Mutex  , nonleaf  ,   true,  Monitor::_safepoint_check_never);
 
     def(StringDedupQueue_lock      , Monitor, leaf,        true,  Monitor::_safepoint_check_never);
     def(StringDedupTable_lock      , Mutex  , leaf,        true,  Monitor::_safepoint_check_never);
--- a/hotspot/src/share/vm/runtime/mutexLocker.hpp	Thu Jul 23 11:14:24 2015 +0200
+++ b/hotspot/src/share/vm/runtime/mutexLocker.hpp	Thu Jul 23 11:14:47 2015 +0200
@@ -87,7 +87,6 @@
                                                  // non-Java threads.
                                                  // (see option ExplicitGCInvokesConcurrent)
 extern Mutex*   ParGCRareEvent_lock;             // Synchronizes various (rare) parallel GC ops.
-extern Mutex*   EvacFailureStack_lock;           // guards the evac failure scan stack
 extern Mutex*   Compile_lock;                    // a lock held when Compilation is updating code (used to block CodeCache traversal, CHA updates, etc)
 extern Monitor* MethodCompileQueue_lock;         // a lock held when method compilations are enqueued, dequeued
 extern Monitor* CompileThread_lock;              // a lock held by compile threads during compilation system initialization