3131#include " gc/g1/g1RootClosures.hpp"
3232#include " gc/g1/g1StringDedup.hpp"
3333#include " gc/g1/g1Trace.hpp"
34+ #include " gc/shared/partialArrayTaskStepper.inline.hpp"
3435#include " gc/shared/taskqueue.inline.hpp"
3536#include " memory/allocation.inline.hpp"
3637#include " oops/access.inline.hpp"
3738#include " oops/oop.inline.hpp"
39+ #include " runtime/atomic.hpp"
3840#include " runtime/prefetch.inline.hpp"
3941#include " utilities/globalDefinitions.hpp"
4042#include " utilities/macros.hpp"
5052G1ParScanThreadState::G1ParScanThreadState(G1CollectedHeap* g1h,
5153 G1RedirtyCardsQueueSet* rdcqs,
5254 uint worker_id,
55+ uint n_workers,
5356 size_t young_cset_length,
5457 size_t optional_cset_length)
5558 : _g1h(g1h),
@@ -70,6 +73,8 @@ G1ParScanThreadState::G1ParScanThreadState(G1CollectedHeap* g1h,
7073 _surviving_young_words(NULL ),
7174 _surviving_words_length(young_cset_length + 1 ),
7275 _old_gen_is_full(false ),
76+ _partial_objarray_chunk_size(ParGCArrayScanChunk),
77+ _partial_array_stepper(n_workers),
7378 _num_optional_regions(optional_cset_length),
7479 _numa(g1h->numa ()),
7580 _obj_alloc_stat(NULL )
@@ -212,48 +217,60 @@ void G1ParScanThreadState::do_partial_array(PartialArrayScanTask task) {
212217
213218 assert (_g1h->is_in_reserved (from_obj), " must be in heap." );
214219 assert (from_obj->is_objArray (), " must be obj array" );
215- objArrayOop from_obj_array = objArrayOop (from_obj);
216- // The from-space object contains the real length.
217- int length = from_obj_array->length ();
218-
219220 assert (from_obj->is_forwarded (), " must be forwarded" );
220- oop to_obj = from_obj->forwardee ();
221+
222+ oop to_obj = from_obj->forwardee ();
221223 assert (from_obj != to_obj, " should not be chunking self-forwarded objects" );
222- objArrayOop to_obj_array = objArrayOop (to_obj);
223- // We keep track of the next start index in the length field of the
224- // to-space object.
225- int next_index = to_obj_array->length ();
226- assert (0 <= next_index && next_index < length,
227- " invariant, next index: %d, length: %d" , next_index, length);
228-
229- int start = next_index;
230- int end = length;
231- int remainder = end - start;
232- // We'll try not to push a range that's smaller than ParGCArrayScanChunk.
233- if (remainder > 2 * ParGCArrayScanChunk) {
234- end = start + ParGCArrayScanChunk;
235- to_obj_array->set_length (end);
236- // Push the remainder before we process the range in case another
237- // worker has run out of things to do and can steal it.
224+ assert (to_obj->is_objArray (), " must be obj array" );
225+ objArrayOop to_array = objArrayOop (to_obj);
226+
227+ PartialArrayTaskStepper::Step step
228+ = _partial_array_stepper.next (objArrayOop (from_obj),
229+ to_array,
230+ _partial_objarray_chunk_size);
231+ for (uint i = 0 ; i < step._ncreate ; ++i) {
238232 push_on_queue (ScannerTask (PartialArrayScanTask (from_obj)));
239- } else {
240- assert (length == end, " sanity" );
241- // We'll process the final range for this object. Restore the length
242- // so that the heap remains parsable in case of evacuation failure.
243- to_obj_array->set_length (end);
244233 }
245234
246- HeapRegion* hr = _g1h->heap_region_containing (to_obj );
235+ HeapRegion* hr = _g1h->heap_region_containing (to_array );
247236 G1ScanInYoungSetter x (&_scanner, hr->is_young ());
248- // Process indexes [start,end). It will also process the header
249- // along with the first chunk (i.e., the chunk with start == 0).
250- // Note that at this point the length field of to_obj_array is not
251- // correct given that we are using it to keep track of the next
252- // start index. oop_iterate_range() (thankfully!) ignores the length
253- // field and only relies on the start / end parameters. It does
254- // however return the size of the object which will be incorrect. So
255- // we have to ignore it even if we wanted to use it.
256- to_obj_array->oop_iterate_range (&_scanner, start, end);
237+ // Process claimed task. The length of to_array is not correct, but
238+ // fortunately the iteration ignores the length field and just relies
239+ // on start/end.
240+ to_array->oop_iterate_range (&_scanner,
241+ step._index ,
242+ step._index + _partial_objarray_chunk_size);
243+ }
244+
245+ MAYBE_INLINE_EVACUATION
246+ void G1ParScanThreadState::start_partial_objarray (G1HeapRegionAttr dest_attr,
247+ oop from_obj,
248+ oop to_obj) {
249+ assert (from_obj->is_objArray (), " precondition" );
250+ assert (from_obj->is_forwarded (), " precondition" );
251+ assert (from_obj->forwardee () == to_obj, " precondition" );
252+ assert (from_obj != to_obj, " should not be scanning self-forwarded objects" );
253+ assert (to_obj->is_objArray (), " precondition" );
254+
255+ objArrayOop to_array = objArrayOop (to_obj);
256+
257+ PartialArrayTaskStepper::Step step
258+ = _partial_array_stepper.start (objArrayOop (from_obj),
259+ to_array,
260+ _partial_objarray_chunk_size);
261+
262+ // Push any needed partial scan tasks. Pushed before processing the
263+ // intitial chunk to allow other workers to steal while we're processing.
264+ for (uint i = 0 ; i < step._ncreate ; ++i) {
265+ push_on_queue (ScannerTask (PartialArrayScanTask (from_obj)));
266+ }
267+
268+ G1ScanInYoungSetter x (&_scanner, dest_attr.is_young ());
269+ // Process the initial chunk. No need to process the type in the
270+ // klass, as it will already be handled by processing the built-in
271+ // module. The length of to_array is not correct, but fortunately
272+ // the iteration ignores that length field and relies on start/end.
273+ to_array->oop_iterate_range (&_scanner, 0 , step._index );
257274}
258275
259276MAYBE_INLINE_EVACUATION
@@ -409,7 +426,10 @@ oop G1ParScanThreadState::do_copy_to_survivor_space(G1HeapRegionAttr const regio
409426 assert (region_attr.is_in_cset (),
410427 " Unexpected region attr type: %s" , region_attr.get_type_str ());
411428
412- const size_t word_sz = old->size ();
429+ // Get the klass once. We'll need it again later, and this avoids
430+ // re-decoding when it's compressed.
431+ Klass* klass = old->klass ();
432+ const size_t word_sz = old->size_given_klass (klass);
413433
414434 uint age = 0 ;
415435 G1HeapRegionAttr dest_attr = next_region_attr (region_attr, old_mark, age);
@@ -476,6 +496,20 @@ oop G1ParScanThreadState::do_copy_to_survivor_space(G1HeapRegionAttr const regio
476496 obj->set_mark_raw (old_mark);
477497 }
478498
499+ // Most objects are not arrays, so do one array check rather than
500+ // checking for each array category for each object.
501+ if (klass->is_array_klass ()) {
502+ if (klass->is_objArray_klass ()) {
503+ start_partial_objarray (dest_attr, old, obj);
504+ } else {
505+ // Nothing needs to be done for typeArrays. Body doesn't contain
506+ // any oops to scan, and the type in the klass will already be handled
507+ // by processing the built-in module.
508+ assert (klass->is_typeArray_klass (), " invariant" );
509+ }
510+ return obj;
511+ }
512+
479513 if (G1StringDedup::is_enabled ()) {
480514 const bool is_from_young = region_attr.is_young ();
481515 const bool is_to_young = dest_attr.is_young ();
@@ -489,17 +523,10 @@ oop G1ParScanThreadState::do_copy_to_survivor_space(G1HeapRegionAttr const regio
489523 obj);
490524 }
491525
492- if (obj->is_objArray () && arrayOop (obj)->length () >= ParGCArrayScanChunk) {
493- // We keep track of the next start index in the length field of
494- // the to-space object. The actual length can be found in the
495- // length field of the from-space object.
496- arrayOop (obj)->set_length (0 );
497- do_partial_array (PartialArrayScanTask (old));
498- } else {
499- G1ScanInYoungSetter x (&_scanner, dest_attr.is_young ());
500- obj->oop_iterate_backwards (&_scanner);
501- }
526+ G1ScanInYoungSetter x (&_scanner, dest_attr.is_young ());
527+ obj->oop_iterate_backwards (&_scanner);
502528 return obj;
529+
503530 } else {
504531 _plab_allocator->undo_allocation (dest_attr, obj_ptr, word_sz, node_index);
505532 return forward_ptr;
@@ -518,7 +545,9 @@ G1ParScanThreadState* G1ParScanThreadStateSet::state_for_worker(uint worker_id)
518545 assert (worker_id < _n_workers, " out of bounds access" );
519546 if (_states[worker_id] == NULL ) {
520547 _states[worker_id] =
521- new G1ParScanThreadState (_g1h, _rdcqs, worker_id, _young_cset_length, _optional_cset_length);
548+ new G1ParScanThreadState (_g1h, _rdcqs,
549+ worker_id, _n_workers,
550+ _young_cset_length, _optional_cset_length);
522551 }
523552 return _states[worker_id];
524553}
0 commit comments