< prev index next >
src/hotspot/share/gc/g1/g1ParScanThreadState.cpp
Print this page
rev 60648 : [mq]: partial_arrays
*** 58,67 ****
--- 58,69 ----
_trim_ticks(),
_surviving_young_words_base(NULL),
_surviving_young_words(NULL),
_surviving_words_length(young_cset_length + 1),
_old_gen_is_full(false),
+ _objarray_scan_chunk_size(ParGCArrayScanChunk),
+ _objarray_length_offset_in_bytes(arrayOopDesc::length_offset_in_bytes()),
_num_optional_regions(optional_cset_length),
_numa(g1h->numa()),
_obj_alloc_stat(NULL)
{
// We allocate number of young gen regions in the collection set plus one
*** 197,248 ****
void G1ParScanThreadState::do_partial_array(PartialArrayScanTask task) {
oop from_obj = task.to_source_array();
assert(_g1h->is_in_reserved(from_obj), "must be in heap.");
assert(from_obj->is_objArray(), "must be obj array");
- objArrayOop from_obj_array = objArrayOop(from_obj);
- // The from-space object contains the real length.
- int length = from_obj_array->length();
-
assert(from_obj->is_forwarded(), "must be forwarded");
oop to_obj = from_obj->forwardee();
assert(from_obj != to_obj, "should not be chunking self-forwarded objects");
! objArrayOop to_obj_array = objArrayOop(to_obj);
! // We keep track of the next start index in the length field of the
! // to-space object.
! int next_index = to_obj_array->length();
! assert(0 <= next_index && next_index < length,
! "invariant, next index: %d, length: %d", next_index, length);
!
! int start = next_index;
! int end = length;
! int remainder = end - start;
! // We'll try not to push a range that's smaller than ParGCArrayScanChunk.
! if (remainder > 2 * ParGCArrayScanChunk) {
! end = start + ParGCArrayScanChunk;
! to_obj_array->set_length(end);
! // Push the remainder before we process the range in case another
! // worker has run out of things to do and can steal it.
! push_on_queue(ScannerTask(PartialArrayScanTask(from_obj)));
! } else {
! assert(length == end, "sanity");
! // We'll process the final range for this object. Restore the length
! // so that the heap remains parsable in case of evacuation failure.
! to_obj_array->set_length(end);
! }
! HeapRegion* hr = _g1h->heap_region_containing(to_obj);
G1ScanInYoungSetter x(&_scanner, hr->is_young());
! // Process indexes [start,end). It will also process the header
! // along with the first chunk (i.e., the chunk with start == 0).
! // Note that at this point the length field of to_obj_array is not
! // correct given that we are using it to keep track of the next
! // start index. oop_iterate_range() (thankfully!) ignores the length
! // field and only relies on the start / end parameters. It does
! // however return the size of the object which will be incorrect. So
! // we have to ignore it even if we wanted to use it.
! to_obj_array->oop_iterate_range(&_scanner, start, end);
}
void G1ParScanThreadState::dispatch_task(ScannerTask task) {
verify_task(task);
if (task.is_narrow_oop_ptr()) {
--- 199,278 ----
void G1ParScanThreadState::do_partial_array(PartialArrayScanTask task) {
oop from_obj = task.to_source_array();
assert(_g1h->is_in_reserved(from_obj), "must be in heap.");
assert(from_obj->is_objArray(), "must be obj array");
assert(from_obj->is_forwarded(), "must be forwarded");
+
oop to_obj = from_obj->forwardee();
assert(from_obj != to_obj, "should not be chunking self-forwarded objects");
! assert(to_obj->is_objArray(), "must be obj array");
! objArrayOop to_array = objArrayOop(to_obj);
!
! // The next chunk index is in the length field of the to-space object.
! // Atomically increment by the chunk size to claim the associated chunk.
! char* to_addr = cast_from_oop<char*>(to_array);
! char* length_addr_raw = (to_addr + _objarray_length_offset_in_bytes);
! volatile int* length_addr = reinterpret_cast<int*>(length_addr_raw);
! int end = Atomic::add(length_addr, _objarray_scan_chunk_size, memory_order_relaxed);
! #ifdef ASSERT
! // The from-space object contains the real length.
! int length = objArrayOop(from_obj)->length();
! assert(end <= length, "invariant: end %d, length %d", end, length);
! assert(((length - end) % _objarray_scan_chunk_size) == 0,
! "invariant: end %d, length %d, chunk size %d",
! end, length, _objarray_scan_chunk_size);
! #endif // ASSERT
! HeapRegion* hr = _g1h->heap_region_containing(to_array);
G1ScanInYoungSetter x(&_scanner, hr->is_young());
! // Process claimed chunk. Note that the length field of
! // to_obj_array is not correct. Fortunately, the iteration ignores
! // the length and just relies on start / end. However, it does
! // return the (incorrect) length, but we ignore it.
! to_array->oop_iterate_range(&_scanner, end - _objarray_scan_chunk_size, end);
! }
!
! oop G1ParScanThreadState::start_partial_objArray(G1HeapRegionAttr dest_attr,
! oop from_obj,
! oop to_obj) {
! assert(from_obj->is_objArray(), "precondition");
! assert(from_obj->is_forwarded(), "precondition");
! assert(from_obj->forwardee() == to_obj, "precondition");
! assert(from_obj != to_obj, "should not be scanning self-forwarded objects");
! assert(to_obj->is_objArray(), "precondition");
!
! objArrayOop to_array = objArrayOop(to_obj);
!
! int length = objArrayOop(from_obj)->length();
! int chunks = length / _objarray_scan_chunk_size;
! int end = length % _objarray_scan_chunk_size;
! assert(end <= length, "invariant");
! assert(((length - end) % _objarray_scan_chunk_size) == 0, "invariant");
! // The value of end can be 0, either because of a 0-length array or
! // because length is a multiple of the chunk size. Both of those
! // are rare and handled in the normal course of the iteration, so
! // not worth doing anything special about here.
!
! // Set to's length to end of initial chunk. Partial tasks use that
! // length field as the start of the next chunk to process. Must be
! // done before enqueuing partial scan tasks, in case other threads
! // steal any of those tasks.
! to_array->set_length(end);
! // Push partial scan tasks for all but the initial chunk. Pushed
! // before processing the initial chunk to allow other workers to
! // steal while we're processing.
! for (int i = 0; i < chunks; ++i) {
! push_on_queue(ScannerTask(PartialArrayScanTask(from_obj)));
! }
! G1ScanInYoungSetter x(&_scanner, dest_attr.is_young());
! // Process the initial chunk. No need to process the type in the
! // klass, as it will already be handled by processing the built-in
! // module. The length of to_array is not correct, but fortunately
! // the iteration ignores that length field and relies on start/end.
! to_array->oop_iterate_range(&_scanner, 0, end);
! return to_array;
}
void G1ParScanThreadState::dispatch_task(ScannerTask task) {
verify_task(task);
if (task.is_narrow_oop_ptr()) {
*** 392,402 ****
oop const old,
markWord const old_mark) {
assert(region_attr.is_in_cset(),
"Unexpected region attr type: %s", region_attr.get_type_str());
! const size_t word_sz = old->size();
uint age = 0;
G1HeapRegionAttr dest_attr = next_region_attr(region_attr, old_mark, age);
HeapRegion* const from_region = _g1h->heap_region_containing(old);
uint node_index = from_region->node_index();
--- 422,435 ----
oop const old,
markWord const old_mark) {
assert(region_attr.is_in_cset(),
"Unexpected region attr type: %s", region_attr.get_type_str());
! // Get the klass once. We'll need it again later, and this avoids
! // re-decoding when it's compressed.
! Klass* klass = old->klass();
! const size_t word_sz = old->size_given_klass(klass);
uint age = 0;
G1HeapRegionAttr dest_attr = next_region_attr(region_attr, old_mark, age);
HeapRegion* const from_region = _g1h->heap_region_containing(old);
uint node_index = from_region->node_index();
*** 459,469 ****
_age_table.add(age, word_sz);
} else {
obj->set_mark_raw(old_mark);
}
! if (G1StringDedup::is_enabled()) {
const bool is_from_young = region_attr.is_young();
const bool is_to_young = dest_attr.is_young();
assert(is_from_young == from_region->is_young(),
"sanity");
assert(is_to_young == _g1h->heap_region_containing(obj)->is_young(),
--- 492,517 ----
_age_table.add(age, word_sz);
} else {
obj->set_mark_raw(old_mark);
}
! // Most objects are not arrays, so do one array check rather than both
! // typeArray and objArray checks for each object.
! if (klass->is_array_klass()) {
! if (klass->is_typeArray_klass()) {
! // Nothing needs to be done for typeArrays. Body doesn't contain
! // any oops to scan, and the type in the klass will already be handled
! // by processing the built-in module.
! return obj;
! } else if (klass->is_objArray_klass()) {
! // Do special handling for objArray.
! return start_partial_objArray(dest_attr, old, obj);
! }
! // Not a special array, so fall through to generic handling.
! }
!
! if (G1StringDedup::is_enabled() && (klass == SystemDictionary::String_klass())) {
const bool is_from_young = region_attr.is_young();
const bool is_to_young = dest_attr.is_young();
assert(is_from_young == from_region->is_young(),
"sanity");
assert(is_to_young == _g1h->heap_region_containing(obj)->is_young(),
*** 472,492 ****
is_to_young,
_worker_id,
obj);
}
- if (obj->is_objArray() && arrayOop(obj)->length() >= ParGCArrayScanChunk) {
- // We keep track of the next start index in the length field of
- // the to-space object. The actual length can be found in the
- // length field of the from-space object.
- arrayOop(obj)->set_length(0);
- do_partial_array(PartialArrayScanTask(old));
- } else {
G1ScanInYoungSetter x(&_scanner, dest_attr.is_young());
obj->oop_iterate_backwards(&_scanner);
- }
return obj;
} else {
_plab_allocator->undo_allocation(dest_attr, obj_ptr, word_sz, node_index);
return forward_ptr;
}
}
--- 520,533 ----
is_to_young,
_worker_id,
obj);
}
G1ScanInYoungSetter x(&_scanner, dest_attr.is_young());
obj->oop_iterate_backwards(&_scanner);
return obj;
+
} else {
_plab_allocator->undo_allocation(dest_attr, obj_ptr, word_sz, node_index);
return forward_ptr;
}
}
< prev index next >