JDK-8003235 : G1: Parallelize displaced header restoration during evacuation failures
  • Type: Enhancement
  • Component: hotspot
  • Sub-Component: gc
  • Affected Version: 8,9
  • Priority: P4
  • Status: Resolved
  • Resolution: Duplicate
  • OS: generic
  • CPU: generic
  • Submitted: 2012-11-09
  • Updated: 2016-02-17
  • Resolved: 2015-11-12
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 9
9.1Resolved
Related Reports
Duplicate :  
Relates :  
Description
When G1 experiences an evacuation failure objects for which we can't allocate space are forwarded to themselves. If an object has a non-standard header (e.g. it was locked or its hash value has been calculated), the object and its header value are recorded (in lock-step) in to a data structure (currently a growable array). After all the objects have been copied and scanned, the data structure where we have recorded the headers is walked and the displaced object headers are restored.

Currently the data structure in which we hold the displaced headers and the objects to which they belong is a global data structure (shared among the GC threads) and the restoration of the object headers is single-threaded.

The worst case latency (i.e. handling an evacuation failure) should be improved if the restoration of the displaced object headers was wrapped into a task that could be executed in parallel by the GC workers.

Comments
Duplicate of 8142404
12-11-2015

Has not been fixed. Currently void G1CollectedHeap::remove_self_forwarding_pointers() { double remove_self_forwards_start = os::elapsedTime(); G1ParRemoveSelfForwardPtrsTask rsfp_task; workers()->run_task(&rsfp_task); // Now restore saved marks, if any. 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); }
10-11-2015

If we look at copy_to_survivor_space: template <bool do_gen_barrier, G1Barrier barrier, bool do_mark_object> oop G1ParCopyClosure<do_gen_barrier, barrier, do_mark_object> ::copy_to_survivor_space(oop old) { size_t word_sz = old->size(); HeapRegion* from_region = _g1->heap_region_containing_raw(old); // +1 to make the -1 indexes valid... int young_index = from_region->young_index_in_cset()+1; assert( (from_region->is_young() && young_index > 0) || (!from_region->is_young() && young_index == 0), "invariant" ); G1CollectorPolicy* g1p = _g1->g1_policy(); markOop m = old->mark(); int age = m->has_displaced_mark_helper() ? m->displaced_mark_helper()->age() : m->age(); GCAllocPurpose alloc_purpose = g1p->evacuation_destination(from_region, age, word_sz); HeapWord* obj_ptr = _par_scan_state->allocate(alloc_purpose, word_sz); #ifndef PRODUCT // Should this evacuation fail? if (_g1->evacuation_should_fail()) { if (obj_ptr != NULL) { _par_scan_state->undo_allocation(alloc_purpose, obj_ptr, word_sz); obj_ptr = NULL; } } #endif // !PRODUCT if (obj_ptr == NULL) { // This will either forward-to-self, or detect that someone else has // installed a forwarding pointer. OopsInHeapRegionClosure* cl = _par_scan_state->evac_failure_closure(); return _g1->handle_evacuation_failure_par(cl, old); } For every object we fail to copy we end up calling G1CollectedHeap::handle_evacuation_failure_par(). This routine first attempts to forward the object to itself. If successful, it then locks a global lock, sets a global variable (holding the evacuation failure copy closure), records the object and it's mark word on to two global arrays, and pushes the failed object on to the copy closures scan stack. Near the end of the pause, we fix up the self-forwarded objects in parallel but the objects which had non-trivial headers are fixed up serially. Check out G1CollectedHeap::remove_self_forwarding_pointers(). It would be nice to parallelize this. For example - as a first step I don't see why we need to hold the copy closure in a global variable. Each thread has its own evacuation failure copy closure (they are allocated on stack in G1ParTask::work()) so we could pass it as a parameter and only take the lock when we need to push an object and its header onto the global arrays. The second step would be have thread specific structures to hold the object and its non-trivial header and then define a parallel task to restore them. It might even be possible to incorporate it into the existing parallel task.
14-11-2012

Whatever data structure is used to handle object/displaced header correlation should address the issue in JDK-8002144 as well as being amenable for processing in a parallel task.
09-11-2012

Charlie Hunt (at Salesforce) has indicated that he would be interested in working this RFE.
09-11-2012