# HG changeset patch # User jmasa # Date 1229125066 28800 # Node ID ffe19141e312329101160fda3e3a2d5aed4a53c4 # Parent 06d2c3204df47646aca909838d27bd0afc692cfd# Parent 0f773163217db0b2a7bf6bb8030647b74f3d07b6 Merge diff -r 06d2c3204df4 -r ffe19141e312 src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp --- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp Fri Dec 12 10:19:39 2008 -0800 +++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp Fri Dec 12 15:37:46 2008 -0800 @@ -2954,7 +2954,7 @@ // The object has been either evacuated or is dead. Fill it with a // dummy object. MemRegion mr((HeapWord*)obj, obj->size()); - SharedHeap::fill_region_with_object(mr); + CollectedHeap::fill_with_object(mr); _cm->clearRangeBothMaps(mr); } } @@ -3225,7 +3225,7 @@ // Otherwise, try to claim it. block = r->par_allocate(free_words); } while (block == NULL); - SharedHeap::fill_region_with_object(MemRegion(block, free_words)); + fill_with_object(block, free_words); } #define use_local_bitmaps 1 @@ -3619,9 +3619,8 @@ guarantee(alloc_buffer(purpose)->contains(obj + word_sz - 1), "should contain whole object"); alloc_buffer(purpose)->undo_allocation(obj, word_sz); - } - else { - SharedHeap::fill_region_with_object(MemRegion(obj, word_sz)); + } else { + CollectedHeap::fill_with_object(obj, word_sz); add_to_undo_waste(word_sz); } } diff -r 06d2c3204df4 -r ffe19141e312 src/share/vm/gc_implementation/g1/heapRegionSeq.cpp --- a/src/share/vm/gc_implementation/g1/heapRegionSeq.cpp Fri Dec 12 10:19:39 2008 -0800 +++ b/src/share/vm/gc_implementation/g1/heapRegionSeq.cpp Fri Dec 12 15:37:46 2008 -0800 @@ -102,7 +102,7 @@ HeapWord* tmp = hr->allocate(sz); assert(tmp != NULL, "Humongous allocation failure"); MemRegion mr = MemRegion(tmp, sz); - SharedHeap::fill_region_with_object(mr); + CollectedHeap::fill_with_object(mr); hr->declare_filled_region_to_BOT(mr); if (i == first) { first_hr->set_startsHumongous(); diff -r 06d2c3204df4 -r ffe19141e312 src/share/vm/gc_implementation/parNew/parGCAllocBuffer.cpp --- a/src/share/vm/gc_implementation/parNew/parGCAllocBuffer.cpp Fri Dec 12 10:19:39 2008 -0800 +++ b/src/share/vm/gc_implementation/parNew/parGCAllocBuffer.cpp Fri Dec 12 15:37:46 2008 -0800 @@ -51,14 +51,14 @@ if (_retained) { // If the buffer had been retained shorten the previous filler object. assert(_retained_filler.end() <= _top, "INVARIANT"); - SharedHeap::fill_region_with_object(_retained_filler); + CollectedHeap::fill_with_object(_retained_filler); // Wasted space book-keeping, otherwise (normally) done in invalidate() _wasted += _retained_filler.word_size(); _retained = false; } assert(!end_of_gc || !_retained, "At this point, end_of_gc ==> !_retained."); if (_top < _hard_end) { - SharedHeap::fill_region_with_object(MemRegion(_top, _hard_end)); + CollectedHeap::fill_with_object(_top, _hard_end); if (!retain) { invalidate(); } else { @@ -155,7 +155,7 @@ // modifying the _next_threshold state in the BOT. void ParGCAllocBufferWithBOT::fill_region_with_block(MemRegion mr, bool contig) { - SharedHeap::fill_region_with_object(mr); + CollectedHeap::fill_with_object(mr); if (contig) { _bt.alloc_block(mr.start(), mr.end()); } else { @@ -171,7 +171,7 @@ "or else _true_end should be equal to _hard_end"); assert(_retained, "or else _true_end should be equal to _hard_end"); assert(_retained_filler.end() <= _top, "INVARIANT"); - SharedHeap::fill_region_with_object(_retained_filler); + CollectedHeap::fill_with_object(_retained_filler); if (_top < _hard_end) { fill_region_with_block(MemRegion(_top, _hard_end), true); } @@ -316,11 +316,9 @@ while (_top <= chunk_boundary) { assert(pointer_delta(_hard_end, chunk_boundary) >= AlignmentReserve, "Consequence of last card handling above."); - MemRegion chunk_portion(chunk_boundary, _hard_end); - _bt.BlockOffsetArray::alloc_block(chunk_portion.start(), - chunk_portion.end()); - SharedHeap::fill_region_with_object(chunk_portion); - _hard_end = chunk_portion.start(); + _bt.BlockOffsetArray::alloc_block(chunk_boundary, _hard_end); + CollectedHeap::fill_with_object(chunk_boundary, _hard_end); + _hard_end = chunk_boundary; chunk_boundary -= ChunkSizeInWords; } _end = _hard_end - AlignmentReserve; diff -r 06d2c3204df4 -r ffe19141e312 src/share/vm/gc_implementation/parNew/parNewGeneration.cpp --- a/src/share/vm/gc_implementation/parNew/parNewGeneration.cpp Fri Dec 12 10:19:39 2008 -0800 +++ b/src/share/vm/gc_implementation/parNew/parNewGeneration.cpp Fri Dec 12 15:37:46 2008 -0800 @@ -201,7 +201,7 @@ "Should contain whole object."); to_space_alloc_buffer()->undo_allocation(obj, word_sz); } else { - SharedHeap::fill_region_with_object(MemRegion(obj, word_sz)); + CollectedHeap::fill_with_object(obj, word_sz); } } diff -r 06d2c3204df4 -r ffe19141e312 src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp --- a/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp Fri Dec 12 10:19:39 2008 -0800 +++ b/src/share/vm/gc_implementation/parallelScavenge/psMarkSweep.cpp Fri Dec 12 15:37:46 2008 -0800 @@ -389,7 +389,7 @@ // full GC. const size_t alignment = old_gen->virtual_space()->alignment(); const size_t eden_used = eden_space->used_in_bytes(); - const size_t promoted = (size_t)(size_policy->avg_promoted()->padded_average()); + const size_t promoted = (size_t)size_policy->avg_promoted()->padded_average(); const size_t absorb_size = align_size_up(eden_used + promoted, alignment); const size_t eden_capacity = eden_space->capacity_in_bytes(); @@ -416,16 +416,14 @@ // Fill the unused part of the old gen. MutableSpace* const old_space = old_gen->object_space(); - MemRegion old_gen_unused(old_space->top(), old_space->end()); + HeapWord* const unused_start = old_space->top(); + size_t const unused_words = pointer_delta(old_space->end(), unused_start); - // If the unused part of the old gen cannot be filled, skip - // absorbing eden. - if (old_gen_unused.word_size() < SharedHeap::min_fill_size()) { - return false; - } - - if (!old_gen_unused.is_empty()) { - SharedHeap::fill_region_with_object(old_gen_unused); + if (unused_words > 0) { + if (unused_words < CollectedHeap::min_fill_size()) { + return false; // If the old gen cannot be filled, must give up. + } + CollectedHeap::fill_with_objects(unused_start, unused_words); } // Take the live data from eden and set both top and end in the old gen to @@ -441,9 +439,8 @@ // Update the object start array for the filler object and the data from eden. ObjectStartArray* const start_array = old_gen->start_array(); - HeapWord* const start = old_gen_unused.start(); - for (HeapWord* addr = start; addr < new_top; addr += oop(addr)->size()) { - start_array->allocate_block(addr); + for (HeapWord* p = unused_start; p < new_top; p += oop(p)->size()) { + start_array->allocate_block(p); } // Could update the promoted average here, but it is not typically updated at diff -r 06d2c3204df4 -r ffe19141e312 src/share/vm/gc_implementation/parallelScavenge/psMarkSweepDecorator.cpp --- a/src/share/vm/gc_implementation/parallelScavenge/psMarkSweepDecorator.cpp Fri Dec 12 10:19:39 2008 -0800 +++ b/src/share/vm/gc_implementation/parallelScavenge/psMarkSweepDecorator.cpp Fri Dec 12 15:37:46 2008 -0800 @@ -275,22 +275,9 @@ HeapWord* q, size_t deadlength) { if (allowed_deadspace_words >= deadlength) { allowed_deadspace_words -= deadlength; - oop(q)->set_mark(markOopDesc::prototype()->set_marked()); - const size_t aligned_min_int_array_size = - align_object_size(typeArrayOopDesc::header_size(T_INT)); - if (deadlength >= aligned_min_int_array_size) { - oop(q)->set_klass(Universe::intArrayKlassObj()); - assert(((deadlength - aligned_min_int_array_size) * (HeapWordSize/sizeof(jint))) < (size_t)max_jint, - "deadspace too big for Arrayoop"); - typeArrayOop(q)->set_length((int)((deadlength - aligned_min_int_array_size) - * (HeapWordSize/sizeof(jint)))); - } else { - assert((int) deadlength == instanceOopDesc::header_size(), - "size for smallest fake dead object doesn't match"); - oop(q)->set_klass(SystemDictionary::object_klass()); - } - assert((int) deadlength == oop(q)->size(), - "make sure size for fake dead object match"); + CollectedHeap::fill_with_object(q, deadlength); + oop(q)->set_mark(oop(q)->mark()->set_marked()); + assert((int) deadlength == oop(q)->size(), "bad filler object size"); // Recall that we required "q == compaction_top". return true; } else { diff -r 06d2c3204df4 -r ffe19141e312 src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp --- a/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp Fri Dec 12 10:19:39 2008 -0800 +++ b/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp Fri Dec 12 15:37:46 2008 -0800 @@ -88,6 +88,72 @@ GrowableArray * PSParallelCompact::_last_gc_live_oops_size = NULL; #endif +void SplitInfo::record(size_t src_region_idx, size_t partial_obj_size, + HeapWord* destination) +{ + assert(src_region_idx != 0, "invalid src_region_idx"); + assert(partial_obj_size != 0, "invalid partial_obj_size argument"); + assert(destination != NULL, "invalid destination argument"); + + _src_region_idx = src_region_idx; + _partial_obj_size = partial_obj_size; + _destination = destination; + + // These fields may not be updated below, so make sure they're clear. + assert(_dest_region_addr == NULL, "should have been cleared"); + assert(_first_src_addr == NULL, "should have been cleared"); + + // Determine the number of destination regions for the partial object. + HeapWord* const last_word = destination + partial_obj_size - 1; + const ParallelCompactData& sd = PSParallelCompact::summary_data(); + HeapWord* const beg_region_addr = sd.region_align_down(destination); + HeapWord* const end_region_addr = sd.region_align_down(last_word); + + if (beg_region_addr == end_region_addr) { + // One destination region. + _destination_count = 1; + if (end_region_addr == destination) { + // The destination falls on a region boundary, thus the first word of the + // partial object will be the first word copied to the destination region. + _dest_region_addr = end_region_addr; + _first_src_addr = sd.region_to_addr(src_region_idx); + } + } else { + // Two destination regions. When copied, the partial object will cross a + // destination region boundary, so a word somewhere within the partial + // object will be the first word copied to the second destination region. + _destination_count = 2; + _dest_region_addr = end_region_addr; + const size_t ofs = pointer_delta(end_region_addr, destination); + assert(ofs < _partial_obj_size, "sanity"); + _first_src_addr = sd.region_to_addr(src_region_idx) + ofs; + } +} + +void SplitInfo::clear() +{ + _src_region_idx = 0; + _partial_obj_size = 0; + _destination = NULL; + _destination_count = 0; + _dest_region_addr = NULL; + _first_src_addr = NULL; + assert(!is_valid(), "sanity"); +} + +#ifdef ASSERT +void SplitInfo::verify_clear() +{ + assert(_src_region_idx == 0, "not clear"); + assert(_partial_obj_size == 0, "not clear"); + assert(_destination == NULL, "not clear"); + assert(_destination_count == 0, "not clear"); + assert(_dest_region_addr == NULL, "not clear"); + assert(_first_src_addr == NULL, "not clear"); +} +#endif // #ifdef ASSERT + + #ifndef PRODUCT const char* PSParallelCompact::space_names[] = { "perm", "old ", "eden", "from", "to " @@ -416,21 +482,134 @@ } } -bool ParallelCompactData::summarize(HeapWord* target_beg, HeapWord* target_end, - HeapWord* source_beg, HeapWord* source_end, - HeapWord** target_next, - HeapWord** source_next) { - // This is too strict. - // assert(region_offset(source_beg) == 0, "not RegionSize aligned"); +// Find the point at which a space can be split and, if necessary, record the +// split point. +// +// If the current src region (which overflowed the destination space) doesn't +// have a partial object, the split point is at the beginning of the current src +// region (an "easy" split, no extra bookkeeping required). +// +// If the current src region has a partial object, the split point is in the +// region where that partial object starts (call it the split_region). If +// split_region has a partial object, then the split point is just after that +// partial object (a "hard" split where we have to record the split data and +// zero the partial_obj_size field). With a "hard" split, we know that the +// partial_obj ends within split_region because the partial object that caused +// the overflow starts in split_region. If split_region doesn't have a partial +// obj, then the split is at the beginning of split_region (another "easy" +// split). +HeapWord* +ParallelCompactData::summarize_split_space(size_t src_region, + SplitInfo& split_info, + HeapWord* destination, + HeapWord* target_end, + HeapWord** target_next) +{ + assert(destination <= target_end, "sanity"); + assert(destination + _region_data[src_region].data_size() > target_end, + "region should not fit into target space"); + + size_t split_region = src_region; + HeapWord* split_destination = destination; + size_t partial_obj_size = _region_data[src_region].partial_obj_size(); + + if (destination + partial_obj_size > target_end) { + // The split point is just after the partial object (if any) in the + // src_region that contains the start of the object that overflowed the + // destination space. + // + // Find the start of the "overflow" object and set split_region to the + // region containing it. + HeapWord* const overflow_obj = _region_data[src_region].partial_obj_addr(); + split_region = addr_to_region_idx(overflow_obj); + + // Clear the source_region field of all destination regions whose first word + // came from data after the split point (a non-null source_region field + // implies a region must be filled). + // + // An alternative to the simple loop below: clear during post_compact(), + // which uses memcpy instead of individual stores, and is easy to + // parallelize. (The downside is that it clears the entire RegionData + // object as opposed to just one field.) + // + // post_compact() would have to clear the summary data up to the highest + // address that was written during the summary phase, which would be + // + // max(top, max(new_top, clear_top)) + // + // where clear_top is a new field in SpaceInfo. Would have to set clear_top + // to destination + partial_obj_size, where both have the values passed to + // this routine. + const RegionData* const sr = region(split_region); + const size_t beg_idx = + addr_to_region_idx(region_align_up(sr->destination() + + sr->partial_obj_size())); + const size_t end_idx = + addr_to_region_idx(region_align_up(destination + partial_obj_size)); + + if (TraceParallelOldGCSummaryPhase) { + gclog_or_tty->print_cr("split: clearing source_region field in [" + SIZE_FORMAT ", " SIZE_FORMAT ")", + beg_idx, end_idx); + } + for (size_t idx = beg_idx; idx < end_idx; ++idx) { + _region_data[idx].set_source_region(0); + } + + // Set split_destination and partial_obj_size to reflect the split region. + split_destination = sr->destination(); + partial_obj_size = sr->partial_obj_size(); + } + + // The split is recorded only if a partial object extends onto the region. + if (partial_obj_size != 0) { + _region_data[split_region].set_partial_obj_size(0); + split_info.record(split_region, partial_obj_size, split_destination); + } + + // Setup the continuation addresses. + *target_next = split_destination + partial_obj_size; + HeapWord* const source_next = region_to_addr(split_region) + partial_obj_size; if (TraceParallelOldGCSummaryPhase) { - tty->print_cr("tb=" PTR_FORMAT " te=" PTR_FORMAT " " - "sb=" PTR_FORMAT " se=" PTR_FORMAT " " - "tn=" PTR_FORMAT " sn=" PTR_FORMAT, - target_beg, target_end, - source_beg, source_end, - target_next != 0 ? *target_next : (HeapWord*) 0, - source_next != 0 ? *source_next : (HeapWord*) 0); + const char * split_type = partial_obj_size == 0 ? "easy" : "hard"; + gclog_or_tty->print_cr("%s split: src=" PTR_FORMAT " src_c=" SIZE_FORMAT + " pos=" SIZE_FORMAT, + split_type, source_next, split_region, + partial_obj_size); + gclog_or_tty->print_cr("%s split: dst=" PTR_FORMAT " dst_c=" SIZE_FORMAT + " tn=" PTR_FORMAT, + split_type, split_destination, + addr_to_region_idx(split_destination), + *target_next); + + if (partial_obj_size != 0) { + HeapWord* const po_beg = split_info.destination(); + HeapWord* const po_end = po_beg + split_info.partial_obj_size(); + gclog_or_tty->print_cr("%s split: " + "po_beg=" PTR_FORMAT " " SIZE_FORMAT " " + "po_end=" PTR_FORMAT " " SIZE_FORMAT, + split_type, + po_beg, addr_to_region_idx(po_beg), + po_end, addr_to_region_idx(po_end)); + } + } + + return source_next; +} + +bool ParallelCompactData::summarize(SplitInfo& split_info, + HeapWord* source_beg, HeapWord* source_end, + HeapWord** source_next, + HeapWord* target_beg, HeapWord* target_end, + HeapWord** target_next) +{ + if (TraceParallelOldGCSummaryPhase) { + HeapWord* const source_next_val = source_next == NULL ? NULL : *source_next; + tty->print_cr("sb=" PTR_FORMAT " se=" PTR_FORMAT " sn=" PTR_FORMAT + "tb=" PTR_FORMAT " te=" PTR_FORMAT " tn=" PTR_FORMAT, + source_beg, source_end, source_next_val, + target_beg, target_end, *target_next); } size_t cur_region = addr_to_region_idx(source_beg); @@ -438,45 +617,53 @@ HeapWord *dest_addr = target_beg; while (cur_region < end_region) { + // The destination must be set even if the region has no data. + _region_data[cur_region].set_destination(dest_addr); + size_t words = _region_data[cur_region].data_size(); - -#if 1 - assert(pointer_delta(target_end, dest_addr) >= words, - "source region does not fit into target region"); -#else - // XXX - need some work on the corner cases here. If the region does not - // fit, then must either make sure any partial_obj from the region fits, or - // "undo" the initial part of the partial_obj that is in the previous - // region. - if (dest_addr + words >= target_end) { - // Let the caller know where to continue. - *target_next = dest_addr; - *source_next = region_to_addr(cur_region); - return false; - } -#endif // #if 1 - - _region_data[cur_region].set_destination(dest_addr); - - // Set the destination_count for cur_region, and if necessary, update - // source_region for a destination region. The source_region field is - // updated if cur_region is the first (left-most) region to be copied to a - // destination region. - // - // The destination_count calculation is a bit subtle. A region that has - // data that compacts into itself does not count itself as a destination. - // This maintains the invariant that a zero count means the region is - // available and can be claimed and then filled. if (words > 0) { + // If cur_region does not fit entirely into the target space, find a point + // at which the source space can be 'split' so that part is copied to the + // target space and the rest is copied elsewhere. + if (dest_addr + words > target_end) { + assert(source_next != NULL, "source_next is NULL when splitting"); + *source_next = summarize_split_space(cur_region, split_info, dest_addr, + target_end, target_next); + return false; + } + + // Compute the destination_count for cur_region, and if necessary, update + // source_region for a destination region. The source_region field is + // updated if cur_region is the first (left-most) region to be copied to a + // destination region. + // + // The destination_count calculation is a bit subtle. A region that has + // data that compacts into itself does not count itself as a destination. + // This maintains the invariant that a zero count means the region is + // available and can be claimed and then filled. + uint destination_count = 0; + if (split_info.is_split(cur_region)) { + // The current region has been split: the partial object will be copied + // to one destination space and the remaining data will be copied to + // another destination space. Adjust the initial destination_count and, + // if necessary, set the source_region field if the partial object will + // cross a destination region boundary. + destination_count = split_info.destination_count(); + if (destination_count == 2) { + size_t dest_idx = addr_to_region_idx(split_info.dest_region_addr()); + _region_data[dest_idx].set_source_region(cur_region); + } + } + HeapWord* const last_addr = dest_addr + words - 1; const size_t dest_region_1 = addr_to_region_idx(dest_addr); const size_t dest_region_2 = addr_to_region_idx(last_addr); -#if 0 + // Initially assume that the destination regions will be the same and // adjust the value below if necessary. Under this assumption, if // cur_region == dest_region_2, then cur_region will be compacted // completely into itself. - uint destination_count = cur_region == dest_region_2 ? 0 : 1; + destination_count += cur_region == dest_region_2 ? 0 : 1; if (dest_region_1 != dest_region_2) { // Destination regions differ; adjust destination_count. destination_count += 1; @@ -487,25 +674,6 @@ // region. _region_data[dest_region_1].set_source_region(cur_region); } -#else - // Initially assume that the destination regions will be different and - // adjust the value below if necessary. Under this assumption, if - // cur_region == dest_region2, then cur_region will be compacted partially - // into dest_region_1 and partially into itself. - uint destination_count = cur_region == dest_region_2 ? 1 : 2; - if (dest_region_1 != dest_region_2) { - // Data from cur_region will be copied to the start of dest_region_2. - _region_data[dest_region_2].set_source_region(cur_region); - } else { - // Destination regions are the same; adjust destination_count. - destination_count -= 1; - if (region_offset(dest_addr) == 0) { - // Data from cur_region will be copied to the start of the destination - // region. - _region_data[dest_region_1].set_source_region(cur_region); - } - } -#endif // #if 0 _region_data[cur_region].set_destination_count(destination_count); _region_data[cur_region].set_data_location(region_to_addr(cur_region)); @@ -749,6 +917,13 @@ const size_t end_region = _summary_data.addr_to_region_idx(_summary_data.region_align_up(max_top)); _summary_data.clear_range(beg_region, end_region); + + // Clear the data used to 'split' regions. + SplitInfo& split_info = _space_info[id].split_info(); + if (split_info.is_valid()) { + split_info.clear(); + } + DEBUG_ONLY(split_info.verify_clear();) } void PSParallelCompact::pre_compact(PreGCValues* pre_gc_values) @@ -807,10 +982,11 @@ { TraceTime tm("post compact", print_phases(), true, gclog_or_tty); - // Clear the marking bitmap and summary data and update top() in each space. for (unsigned int id = perm_space_id; id < last_space_id; ++id) { + // Clear the marking bitmap, summary data and split info. clear_data_covering_space(SpaceId(id)); - _space_info[id].space()->set_top(_space_info[id].new_top()); + // Update top(). Must be done after clearing the bitmap and summary data. + _space_info[id].publish_new_top(); } MutableSpace* const eden_space = _space_info[eden_space_id].space(); @@ -1151,6 +1327,13 @@ PSParallelCompact::compute_dense_prefix(const SpaceId id, bool maximum_compaction) { + if (ParallelOldGCSplitALot) { + if (_space_info[id].dense_prefix() != _space_info[id].space()->bottom()) { + // The value was chosen to provoke splitting a young gen space; use it. + return _space_info[id].dense_prefix(); + } + } + const size_t region_size = ParallelCompactData::RegionSize; const ParallelCompactData& sd = summary_data(); @@ -1239,14 +1422,169 @@ return sd.region_to_addr(best_cp); } +#ifndef PRODUCT +void +PSParallelCompact::fill_with_live_objects(SpaceId id, HeapWord* const start, + size_t words) +{ + if (TraceParallelOldGCSummaryPhase) { + tty->print_cr("fill_with_live_objects [" PTR_FORMAT " " PTR_FORMAT ") " + SIZE_FORMAT, start, start + words, words); + } + + ObjectStartArray* const start_array = _space_info[id].start_array(); + CollectedHeap::fill_with_objects(start, words); + for (HeapWord* p = start; p < start + words; p += oop(p)->size()) { + _mark_bitmap.mark_obj(p, words); + _summary_data.add_obj(p, words); + start_array->allocate_block(p); + } +} + +void +PSParallelCompact::summarize_new_objects(SpaceId id, HeapWord* start) +{ + ParallelCompactData& sd = summary_data(); + MutableSpace* space = _space_info[id].space(); + + // Find the source and destination start addresses. + HeapWord* const src_addr = sd.region_align_down(start); + HeapWord* dst_addr; + if (src_addr < start) { + dst_addr = sd.addr_to_region_ptr(src_addr)->destination(); + } else if (src_addr > space->bottom()) { + // The start (the original top() value) is aligned to a region boundary so + // the associated region does not have a destination. Compute the + // destination from the previous region. + RegionData* const cp = sd.addr_to_region_ptr(src_addr) - 1; + dst_addr = cp->destination() + cp->data_size(); + } else { + // Filling the entire space. + dst_addr = space->bottom(); + } + assert(dst_addr != NULL, "sanity"); + + // Update the summary data. + bool result = _summary_data.summarize(_space_info[id].split_info(), + src_addr, space->top(), NULL, + dst_addr, space->end(), + _space_info[id].new_top_addr()); + assert(result, "should not fail: bad filler object size"); +} + +void +PSParallelCompact::provoke_split(bool & max_compaction) +{ + const size_t region_size = ParallelCompactData::RegionSize; + ParallelCompactData& sd = summary_data(); + + MutableSpace* const eden_space = _space_info[eden_space_id].space(); + MutableSpace* const from_space = _space_info[from_space_id].space(); + const size_t eden_live = pointer_delta(eden_space->top(), + _space_info[eden_space_id].new_top()); + const size_t from_live = pointer_delta(from_space->top(), + _space_info[from_space_id].new_top()); + + const size_t min_fill_size = CollectedHeap::min_fill_size(); + const size_t eden_free = pointer_delta(eden_space->end(), eden_space->top()); + const size_t eden_fillable = eden_free >= min_fill_size ? eden_free : 0; + const size_t from_free = pointer_delta(from_space->end(), from_space->top()); + const size_t from_fillable = from_free >= min_fill_size ? from_free : 0; + + // Choose the space to split; need at least 2 regions live (or fillable). + SpaceId id; + MutableSpace* space; + size_t live_words; + size_t fill_words; + if (eden_live + eden_fillable >= region_size * 2) { + id = eden_space_id; + space = eden_space; + live_words = eden_live; + fill_words = eden_fillable; + } else if (from_live + from_fillable >= region_size * 2) { + id = from_space_id; + space = from_space; + live_words = from_live; + fill_words = from_fillable; + } else { + return; // Give up. + } + assert(fill_words == 0 || fill_words >= min_fill_size, "sanity"); + + if (live_words < region_size * 2) { + // Fill from top() to end() w/live objects of mixed sizes. + HeapWord* const fill_start = space->top(); + live_words += fill_words; + + space->set_top(fill_start + fill_words); + if (ZapUnusedHeapArea) { + space->set_top_for_allocations(); + } + + HeapWord* cur_addr = fill_start; + while (fill_words > 0) { + const size_t r = (size_t)os::random() % (region_size / 2) + min_fill_size; + size_t cur_size = MIN2(align_object_size_(r), fill_words); + if (fill_words - cur_size < min_fill_size) { + cur_size = fill_words; // Avoid leaving a fragment too small to fill. + } + + CollectedHeap::fill_with_object(cur_addr, cur_size); + mark_bitmap()->mark_obj(cur_addr, cur_size); + sd.add_obj(cur_addr, cur_size); + + cur_addr += cur_size; + fill_words -= cur_size; + } + + summarize_new_objects(id, fill_start); + } + + max_compaction = false; + + // Manipulate the old gen so that it has room for about half of the live data + // in the target young gen space (live_words / 2). + id = old_space_id; + space = _space_info[id].space(); + const size_t free_at_end = space->free_in_words(); + const size_t free_target = align_object_size(live_words / 2); + const size_t dead = pointer_delta(space->top(), _space_info[id].new_top()); + + if (free_at_end >= free_target + min_fill_size) { + // Fill space above top() and set the dense prefix so everything survives. + HeapWord* const fill_start = space->top(); + const size_t fill_size = free_at_end - free_target; + space->set_top(space->top() + fill_size); + if (ZapUnusedHeapArea) { + space->set_top_for_allocations(); + } + fill_with_live_objects(id, fill_start, fill_size); + summarize_new_objects(id, fill_start); + _space_info[id].set_dense_prefix(sd.region_align_down(space->top())); + } else if (dead + free_at_end > free_target) { + // Find a dense prefix that makes the right amount of space available. + HeapWord* cur = sd.region_align_down(space->top()); + HeapWord* cur_destination = sd.addr_to_region_ptr(cur)->destination(); + size_t dead_to_right = pointer_delta(space->end(), cur_destination); + while (dead_to_right < free_target) { + cur -= region_size; + cur_destination = sd.addr_to_region_ptr(cur)->destination(); + dead_to_right = pointer_delta(space->end(), cur_destination); + } + _space_info[id].set_dense_prefix(cur); + } +} +#endif // #ifndef PRODUCT + void PSParallelCompact::summarize_spaces_quick() { for (unsigned int i = 0; i < last_space_id; ++i) { const MutableSpace* space = _space_info[i].space(); - bool result = _summary_data.summarize(space->bottom(), space->end(), - space->bottom(), space->top(), - _space_info[i].new_top_addr()); - assert(result, "should never fail"); + HeapWord** nta = _space_info[i].new_top_addr(); + bool result = _summary_data.summarize(_space_info[i].split_info(), + space->bottom(), space->top(), NULL, + space->bottom(), space->end(), nta); + assert(result, "space must fit into itself"); _space_info[i].set_dense_prefix(space->bottom()); } } @@ -1308,8 +1646,7 @@ } #endif // #ifdef _LP64 - MemRegion region(obj_beg, obj_len); - SharedHeap::fill_region_with_object(region); + CollectedHeap::fill_with_object(obj_beg, obj_len); _mark_bitmap.mark_obj(obj_beg, obj_len); _summary_data.add_obj(obj_beg, obj_len); assert(start_array(id) != NULL, "sanity"); @@ -1318,11 +1655,23 @@ } void +PSParallelCompact::clear_source_region(HeapWord* beg_addr, HeapWord* end_addr) +{ + RegionData* const beg_ptr = _summary_data.addr_to_region_ptr(beg_addr); + HeapWord* const end_aligned_up = _summary_data.region_align_up(end_addr); + RegionData* const end_ptr = _summary_data.addr_to_region_ptr(end_aligned_up); + for (RegionData* cur = beg_ptr; cur < end_ptr; ++cur) { + cur->set_source_region(0); + } +} + +void PSParallelCompact::summarize_space(SpaceId id, bool maximum_compaction) { assert(id < last_space_id, "id out of range"); - assert(_space_info[id].dense_prefix() == _space_info[id].space()->bottom(), - "should have been set in summarize_spaces_quick()"); + assert(_space_info[id].dense_prefix() == _space_info[id].space()->bottom() || + ParallelOldGCSplitALot && id == old_space_id, + "should have been reset in summarize_spaces_quick()"); const MutableSpace* space = _space_info[id].space(); if (_space_info[id].new_top() != space->bottom()) { @@ -1338,20 +1687,24 @@ } #endif // #ifndef PRODUCT - // If dead space crosses the dense prefix boundary, it is (at least - // partially) filled with a dummy object, marked live and added to the - // summary data. This simplifies the copy/update phase and must be done - // before the final locations of objects are determined, to prevent leaving - // a fragment of dead space that is too small to fill with an object. + // Recompute the summary data, taking into account the dense prefix. If + // every last byte will be reclaimed, then the existing summary data which + // compacts everything can be left in place. if (!maximum_compaction && dense_prefix_end != space->bottom()) { + // If dead space crosses the dense prefix boundary, it is (at least + // partially) filled with a dummy object, marked live and added to the + // summary data. This simplifies the copy/update phase and must be done + // before the final locations of objects are determined, to prevent + // leaving a fragment of dead space that is too small to fill. fill_dense_prefix_end(id); + + // Compute the destination of each Region, and thus each object. + _summary_data.summarize_dense_prefix(space->bottom(), dense_prefix_end); + _summary_data.summarize(_space_info[id].split_info(), + dense_prefix_end, space->top(), NULL, + dense_prefix_end, space->end(), + _space_info[id].new_top_addr()); } - - // Compute the destination of each Region, and thus each object. - _summary_data.summarize_dense_prefix(space->bottom(), dense_prefix_end); - _summary_data.summarize(dense_prefix_end, space->end(), - dense_prefix_end, space->top(), - _space_info[id].new_top_addr()); } if (TraceParallelOldGCSummaryPhase) { @@ -1371,6 +1724,30 @@ } } +#ifndef PRODUCT +void PSParallelCompact::summary_phase_msg(SpaceId dst_space_id, + HeapWord* dst_beg, HeapWord* dst_end, + SpaceId src_space_id, + HeapWord* src_beg, HeapWord* src_end) +{ + if (TraceParallelOldGCSummaryPhase) { + tty->print_cr("summarizing %d [%s] into %d [%s]: " + "src=" PTR_FORMAT "-" PTR_FORMAT " " + SIZE_FORMAT "-" SIZE_FORMAT " " + "dst=" PTR_FORMAT "-" PTR_FORMAT " " + SIZE_FORMAT "-" SIZE_FORMAT, + src_space_id, space_names[src_space_id], + dst_space_id, space_names[dst_space_id], + src_beg, src_end, + _summary_data.addr_to_region_idx(src_beg), + _summary_data.addr_to_region_idx(src_end), + dst_beg, dst_end, + _summary_data.addr_to_region_idx(dst_beg), + _summary_data.addr_to_region_idx(dst_end)); + } +} +#endif // #ifndef PRODUCT + void PSParallelCompact::summary_phase(ParCompactionManager* cm, bool maximum_compaction) { @@ -1403,57 +1780,98 @@ // The amount of live data that will end up in old space (assuming it fits). size_t old_space_total_live = 0; - unsigned int id; - for (id = old_space_id; id < last_space_id; ++id) { + assert(perm_space_id < old_space_id, "should not count perm data here"); + for (unsigned int id = old_space_id; id < last_space_id; ++id) { old_space_total_live += pointer_delta(_space_info[id].new_top(), _space_info[id].space()->bottom()); } - const MutableSpace* old_space = _space_info[old_space_id].space(); - if (old_space_total_live > old_space->capacity_in_words()) { + MutableSpace* const old_space = _space_info[old_space_id].space(); + const size_t old_capacity = old_space->capacity_in_words(); + if (old_space_total_live > old_capacity) { // XXX - should also try to expand maximum_compaction = true; - } else if (!UseParallelOldGCDensePrefix) { - maximum_compaction = true; } +#ifndef PRODUCT + if (ParallelOldGCSplitALot && old_space_total_live < old_capacity) { + if (total_invocations() % ParallelOldGCSplitInterval == 0) { + provoke_split(maximum_compaction); + } + } +#endif // #ifndef PRODUCT // Permanent and Old generations. summarize_space(perm_space_id, maximum_compaction); summarize_space(old_space_id, maximum_compaction); - // Summarize the remaining spaces (those in the young gen) into old space. If - // the live data from a space doesn't fit, the existing summarization is left - // intact, so the data is compacted down within the space itself. - HeapWord** new_top_addr = _space_info[old_space_id].new_top_addr(); - HeapWord* const target_space_end = old_space->end(); - for (id = eden_space_id; id < last_space_id; ++id) { + // Summarize the remaining spaces in the young gen. The initial target space + // is the old gen. If a space does not fit entirely into the target, then the + // remainder is compacted into the space itself and that space becomes the new + // target. + SpaceId dst_space_id = old_space_id; + HeapWord* dst_space_end = old_space->end(); + HeapWord** new_top_addr = _space_info[dst_space_id].new_top_addr(); + for (unsigned int id = eden_space_id; id < last_space_id; ++id) { const MutableSpace* space = _space_info[id].space(); const size_t live = pointer_delta(_space_info[id].new_top(), space->bottom()); - const size_t available = pointer_delta(target_space_end, *new_top_addr); + const size_t available = pointer_delta(dst_space_end, *new_top_addr); + + NOT_PRODUCT(summary_phase_msg(dst_space_id, *new_top_addr, dst_space_end, + SpaceId(id), space->bottom(), space->top());) if (live > 0 && live <= available) { // All the live data will fit. - if (TraceParallelOldGCSummaryPhase) { - tty->print_cr("summarizing %d into old_space @ " PTR_FORMAT, - id, *new_top_addr); - } - _summary_data.summarize(*new_top_addr, target_space_end, - space->bottom(), space->top(), - new_top_addr); - + bool done = _summary_data.summarize(_space_info[id].split_info(), + space->bottom(), space->top(), + NULL, + *new_top_addr, dst_space_end, + new_top_addr); + assert(done, "space must fit into old gen"); + + // XXX - this is necessary because decrement_destination_counts() tests + // source_region() to determine if a region will be filled. Probably + // better to pass src_space->new_top() into decrement_destination_counts + // and test that instead. + // // Clear the source_region field for each region in the space. - HeapWord* const new_top = _space_info[id].new_top(); - HeapWord* const clear_end = _summary_data.region_align_up(new_top); - RegionData* beg_region = - _summary_data.addr_to_region_ptr(space->bottom()); - RegionData* end_region = _summary_data.addr_to_region_ptr(clear_end); - while (beg_region < end_region) { - beg_region->set_source_region(0); - ++beg_region; - } + clear_source_region(space->bottom(), _space_info[id].new_top()); // Reset the new_top value for the space. _space_info[id].set_new_top(space->bottom()); + } else if (live > 0) { + // Attempt to fit part of the source space into the target space. + HeapWord* next_src_addr = NULL; + bool done = _summary_data.summarize(_space_info[id].split_info(), + space->bottom(), space->top(), + &next_src_addr, + *new_top_addr, dst_space_end, + new_top_addr); + assert(!done, "space should not fit into old gen"); + assert(next_src_addr != NULL, "sanity"); + + // The source space becomes the new target, so the remainder is compacted + // within the space itself. + dst_space_id = SpaceId(id); + dst_space_end = space->end(); + new_top_addr = _space_info[id].new_top_addr(); + HeapWord* const clear_end = _space_info[id].new_top(); + NOT_PRODUCT(summary_phase_msg(dst_space_id, + space->bottom(), dst_space_end, + SpaceId(id), next_src_addr, space->top());) + done = _summary_data.summarize(_space_info[id].split_info(), + next_src_addr, space->top(), + NULL, + space->bottom(), dst_space_end, + new_top_addr); + assert(done, "space must fit when compacted into itself"); + assert(*new_top_addr <= space->top(), "usage should not grow"); + + // XXX - this should go away. See comments above. + // + // Clear the source_region field in regions at the end of the space that + // will not be filled. + HeapWord* const clear_beg = _summary_data.region_align_up(*new_top_addr); + clear_source_region(clear_beg, clear_end); } } @@ -1807,9 +2225,14 @@ // Fill the unused part of the old gen. MutableSpace* const old_space = old_gen->object_space(); - MemRegion old_gen_unused(old_space->top(), old_space->end()); - if (!old_gen_unused.is_empty()) { - SharedHeap::fill_region_with_object(old_gen_unused); + HeapWord* const unused_start = old_space->top(); + size_t const unused_words = pointer_delta(old_space->end(), unused_start); + + if (unused_words > 0) { + if (unused_words < CollectedHeap::min_fill_size()) { + return false; // If the old gen cannot be filled, must give up. + } + CollectedHeap::fill_with_objects(unused_start, unused_words); } // Take the live data from eden and set both top and end in the old gen to @@ -1825,9 +2248,8 @@ // Update the object start array for the filler object and the data from eden. ObjectStartArray* const start_array = old_gen->start_array(); - HeapWord* const start = old_gen_unused.start(); - for (HeapWord* addr = start; addr < new_top; addr += oop(addr)->size()) { - start_array->allocate_block(addr); + for (HeapWord* p = unused_start; p < new_top; p += oop(p)->size()) { + start_array->allocate_block(p); } // Could update the promoted average here, but it is not typically updated at @@ -2048,14 +2470,13 @@ // regions in the dense prefix. Assume that 1 gc thread // will work on opening the gaps and the remaining gc threads // will work on the dense prefix. - SpaceId space_id = old_space_id; - while (space_id != last_space_id) { + unsigned int space_id; + for (space_id = old_space_id; space_id < last_space_id; ++ space_id) { HeapWord* const dense_prefix_end = _space_info[space_id].dense_prefix(); const MutableSpace* const space = _space_info[space_id].space(); if (dense_prefix_end == space->bottom()) { // There is no dense prefix for this space. - space_id = next_compaction_space_id(space_id); continue; } @@ -2105,23 +2526,20 @@ // region_index_end is not processed size_t region_index_end = MIN2(region_index_start + regions_per_thread, region_index_end_dense_prefix); - q->enqueue(new UpdateDensePrefixTask( - space_id, - region_index_start, - region_index_end)); + q->enqueue(new UpdateDensePrefixTask(SpaceId(space_id), + region_index_start, + region_index_end)); region_index_start = region_index_end; } } // This gets any part of the dense prefix that did not // fit evenly. if (region_index_start < region_index_end_dense_prefix) { - q->enqueue(new UpdateDensePrefixTask( - space_id, - region_index_start, - region_index_end_dense_prefix)); + q->enqueue(new UpdateDensePrefixTask(SpaceId(space_id), + region_index_start, + region_index_end_dense_prefix)); } - space_id = next_compaction_space_id(space_id); - } // End tasks for dense prefix + } } void PSParallelCompact::enqueue_region_stealing_tasks( @@ -2567,16 +2985,24 @@ return m->bit_to_addr(cur_beg); } -HeapWord* -PSParallelCompact::first_src_addr(HeapWord* const dest_addr, - size_t src_region_idx) +HeapWord* PSParallelCompact::first_src_addr(HeapWord* const dest_addr, + SpaceId src_space_id, + size_t src_region_idx) { + assert(summary_data().is_region_aligned(dest_addr), "not aligned"); + + const SplitInfo& split_info = _space_info[src_space_id].split_info(); + if (split_info.dest_region_addr() == dest_addr) { + // The partial object ending at the split point contains the first word to + // be copied to dest_addr. + return split_info.first_src_addr(); + } + + const ParallelCompactData& sd = summary_data(); ParMarkBitMap* const bitmap = mark_bitmap(); - const ParallelCompactData& sd = summary_data(); const size_t RegionSize = ParallelCompactData::RegionSize; assert(sd.is_region_aligned(dest_addr), "not aligned"); - const RegionData* const src_region_ptr = sd.region(src_region_idx); const size_t partial_obj_size = src_region_ptr->partial_obj_size(); HeapWord* const src_region_destination = src_region_ptr->destination(); @@ -2737,7 +3163,7 @@ HeapWord* src_space_top = _space_info[src_space_id].space()->top(); MoveAndUpdateClosure closure(bitmap, cm, start_array, dest_addr, words); - closure.set_source(first_src_addr(dest_addr, src_region_idx)); + closure.set_source(first_src_addr(dest_addr, src_space_id, src_region_idx)); // Adjust src_region_idx to prepare for decrementing destination counts (the // destination count is not decremented when a region is copied to itself). @@ -3008,34 +3434,3 @@ summary_data().calc_new_pointer(Universe::intArrayKlassObj()); } -// The initial implementation of this method created a field -// _next_compaction_space_id in SpaceInfo and initialized -// that field in SpaceInfo::initialize_space_info(). That -// required that _next_compaction_space_id be declared a -// SpaceId in SpaceInfo and that would have required that -// either SpaceId be declared in a separate class or that -// it be declared in SpaceInfo. It didn't seem consistent -// to declare it in SpaceInfo (didn't really fit logically). -// Alternatively, defining a separate class to define SpaceId -// seem excessive. This implementation is simple and localizes -// the knowledge. - -PSParallelCompact::SpaceId -PSParallelCompact::next_compaction_space_id(SpaceId id) { - assert(id < last_space_id, "id out of range"); - switch (id) { - case perm_space_id : - return last_space_id; - case old_space_id : - return eden_space_id; - case eden_space_id : - return from_space_id; - case from_space_id : - return to_space_id; - case to_space_id : - return last_space_id; - default: - assert(false, "Bad space id"); - return last_space_id; - } -} diff -r 06d2c3204df4 -r ffe19141e312 src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.hpp --- a/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.hpp Fri Dec 12 10:19:39 2008 -0800 +++ b/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.hpp Fri Dec 12 15:37:46 2008 -0800 @@ -36,6 +36,123 @@ class MoveAndUpdateClosure; class RefProcTaskExecutor; +// The SplitInfo class holds the information needed to 'split' a source region +// so that the live data can be copied to two destination *spaces*. Normally, +// all the live data in a region is copied to a single destination space (e.g., +// everything live in a region in eden is copied entirely into the old gen). +// However, when the heap is nearly full, all the live data in eden may not fit +// into the old gen. Copying only some of the regions from eden to old gen +// requires finding a region that does not contain a partial object (i.e., no +// live object crosses the region boundary) somewhere near the last object that +// does fit into the old gen. Since it's not always possible to find such a +// region, splitting is necessary for predictable behavior. +// +// A region is always split at the end of the partial object. This avoids +// additional tests when calculating the new location of a pointer, which is a +// very hot code path. The partial object and everything to its left will be +// copied to another space (call it dest_space_1). The live data to the right +// of the partial object will be copied either within the space itself, or to a +// different destination space (distinct from dest_space_1). +// +// Split points are identified during the summary phase, when region +// destinations are computed: data about the split, including the +// partial_object_size, is recorded in a SplitInfo record and the +// partial_object_size field in the summary data is set to zero. The zeroing is +// possible (and necessary) since the partial object will move to a different +// destination space than anything to its right, thus the partial object should +// not affect the locations of any objects to its right. +// +// The recorded data is used during the compaction phase, but only rarely: when +// the partial object on the split region will be copied across a destination +// region boundary. This test is made once each time a region is filled, and is +// a simple address comparison, so the overhead is negligible (see +// PSParallelCompact::first_src_addr()). +// +// Notes: +// +// Only regions with partial objects are split; a region without a partial +// object does not need any extra bookkeeping. +// +// At most one region is split per space, so the amount of data required is +// constant. +// +// A region is split only when the destination space would overflow. Once that +// happens, the destination space is abandoned and no other data (even from +// other source spaces) is targeted to that destination space. Abandoning the +// destination space may leave a somewhat large unused area at the end, if a +// large object caused the overflow. +// +// Future work: +// +// More bookkeeping would be required to continue to use the destination space. +// The most general solution would allow data from regions in two different +// source spaces to be "joined" in a single destination region. At the very +// least, additional code would be required in next_src_region() to detect the +// join and skip to an out-of-order source region. If the join region was also +// the last destination region to which a split region was copied (the most +// likely case), then additional work would be needed to get fill_region() to +// stop iteration and switch to a new source region at the right point. Basic +// idea would be to use a fake value for the top of the source space. It is +// doable, if a bit tricky. +// +// A simpler (but less general) solution would fill the remainder of the +// destination region with a dummy object and continue filling the next +// destination region. + +class SplitInfo +{ +public: + // Return true if this split info is valid (i.e., if a split has been + // recorded). The very first region cannot have a partial object and thus is + // never split, so 0 is the 'invalid' value. + bool is_valid() const { return _src_region_idx > 0; } + + // Return true if this split holds data for the specified source region. + inline bool is_split(size_t source_region) const; + + // The index of the split region, the size of the partial object on that + // region and the destination of the partial object. + size_t src_region_idx() const { return _src_region_idx; } + size_t partial_obj_size() const { return _partial_obj_size; } + HeapWord* destination() const { return _destination; } + + // The destination count of the partial object referenced by this split + // (either 1 or 2). This must be added to the destination count of the + // remainder of the source region. + unsigned int destination_count() const { return _destination_count; } + + // If a word within the partial object will be written to the first word of a + // destination region, this is the address of the destination region; + // otherwise this is NULL. + HeapWord* dest_region_addr() const { return _dest_region_addr; } + + // If a word within the partial object will be written to the first word of a + // destination region, this is the address of that word within the partial + // object; otherwise this is NULL. + HeapWord* first_src_addr() const { return _first_src_addr; } + + // Record the data necessary to split the region src_region_idx. + void record(size_t src_region_idx, size_t partial_obj_size, + HeapWord* destination); + + void clear(); + + DEBUG_ONLY(void verify_clear();) + +private: + size_t _src_region_idx; + size_t _partial_obj_size; + HeapWord* _destination; + unsigned int _destination_count; + HeapWord* _dest_region_addr; + HeapWord* _first_src_addr; +}; + +inline bool SplitInfo::is_split(size_t region_idx) const +{ + return _src_region_idx == region_idx && is_valid(); +} + class SpaceInfo { public: @@ -58,18 +175,23 @@ // is no start array. ObjectStartArray* start_array() const { return _start_array; } + SplitInfo& split_info() { return _split_info; } + void set_space(MutableSpace* s) { _space = s; } void set_new_top(HeapWord* addr) { _new_top = addr; } void set_min_dense_prefix(HeapWord* addr) { _min_dense_prefix = addr; } void set_dense_prefix(HeapWord* addr) { _dense_prefix = addr; } void set_start_array(ObjectStartArray* s) { _start_array = s; } + void publish_new_top() const { _space->set_top(_new_top); } + private: MutableSpace* _space; HeapWord* _new_top; HeapWord* _min_dense_prefix; HeapWord* _dense_prefix; ObjectStartArray* _start_array; + SplitInfo _split_info; }; class ParallelCompactData @@ -230,9 +352,14 @@ // must be region-aligned; end need not be. void summarize_dense_prefix(HeapWord* beg, HeapWord* end); - bool summarize(HeapWord* target_beg, HeapWord* target_end, + HeapWord* summarize_split_space(size_t src_region, SplitInfo& split_info, + HeapWord* destination, HeapWord* target_end, + HeapWord** target_next); + bool summarize(SplitInfo& split_info, HeapWord* source_beg, HeapWord* source_end, - HeapWord** target_next, HeapWord** source_next = 0); + HeapWord** source_next, + HeapWord* target_beg, HeapWord* target_end, + HeapWord** target_next); void clear(); void clear_range(size_t beg_region, size_t end_region); @@ -838,13 +965,27 @@ // non-empty. static void fill_dense_prefix_end(SpaceId id); + // Clear the summary data source_region field for the specified addresses. + static void clear_source_region(HeapWord* beg_addr, HeapWord* end_addr); + +#ifndef PRODUCT + // Routines to provoke splitting a young gen space (ParallelOldGCSplitALot). + + // Fill the region [start, start + words) with live object(s). Only usable + // for the old and permanent generations. + static void fill_with_live_objects(SpaceId id, HeapWord* const start, + size_t words); + // Include the new objects in the summary data. + static void summarize_new_objects(SpaceId id, HeapWord* start); + + // Add live objects and/or choose the dense prefix to provoke splitting. + static void provoke_split(bool & maximum_compaction); +#endif + static void summarize_spaces_quick(); static void summarize_space(SpaceId id, bool maximum_compaction); static void summary_phase(ParCompactionManager* cm, bool maximum_compaction); - // The space that is compacted after space_id. - static SpaceId next_compaction_space_id(SpaceId space_id); - // Adjust addresses in roots. Does not adjust addresses in heap. static void adjust_roots(); @@ -999,6 +1140,7 @@ // Return the address of the word to be copied to dest_addr, which must be // aligned to a region boundary. static HeapWord* first_src_addr(HeapWord* const dest_addr, + SpaceId src_space_id, size_t src_region_idx); // Determine the next source region, set closure.source() to the start of the @@ -1081,6 +1223,10 @@ const SpaceId id, const bool maximum_compaction, HeapWord* const addr); + static void summary_phase_msg(SpaceId dst_space_id, + HeapWord* dst_beg, HeapWord* dst_end, + SpaceId src_space_id, + HeapWord* src_beg, HeapWord* src_end); #endif // #ifndef PRODUCT #ifdef ASSERT @@ -1324,31 +1470,28 @@ oop(addr)->update_contents(compaction_manager()); } -class FillClosure: public ParMarkBitMapClosure { - public: +class FillClosure: public ParMarkBitMapClosure +{ +public: FillClosure(ParCompactionManager* cm, PSParallelCompact::SpaceId space_id) : ParMarkBitMapClosure(PSParallelCompact::mark_bitmap(), cm), - _space_id(space_id), - _start_array(PSParallelCompact::start_array(space_id)) { - assert(_space_id == PSParallelCompact::perm_space_id || - _space_id == PSParallelCompact::old_space_id, + _start_array(PSParallelCompact::start_array(space_id)) + { + assert(space_id == PSParallelCompact::perm_space_id || + space_id == PSParallelCompact::old_space_id, "cannot use FillClosure in the young gen"); - assert(bitmap() != NULL, "need a bitmap"); - assert(_start_array != NULL, "need a start array"); - } - - void fill_region(HeapWord* addr, size_t size) { - MemRegion region(addr, size); - SharedHeap::fill_region_with_object(region); - _start_array->allocate_block(addr); } virtual IterationStatus do_addr(HeapWord* addr, size_t size) { - fill_region(addr, size); + CollectedHeap::fill_with_objects(addr, size); + HeapWord* const end = addr + size; + do { + _start_array->allocate_block(addr); + addr += oop(addr)->size(); + } while (addr < end); return ParMarkBitMap::incomplete; } private: - const PSParallelCompact::SpaceId _space_id; - ObjectStartArray* const _start_array; + ObjectStartArray* const _start_array; }; diff -r 06d2c3204df4 -r ffe19141e312 src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.cpp --- a/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.cpp Fri Dec 12 10:19:39 2008 -0800 +++ b/src/share/vm/gc_implementation/parallelScavenge/psPromotionManager.cpp Fri Dec 12 15:37:46 2008 -0800 @@ -499,26 +499,15 @@ // We lost, someone else "owns" this object guarantee(o->is_forwarded(), "Object must be forwarded if the cas failed."); - // Unallocate the space used. NOTE! We may have directly allocated - // the object. If so, we cannot deallocate it, so we have to test! + // Try to deallocate the space. If it was directly allocated we cannot + // deallocate it, so we have to test. If the deallocation fails, + // overwrite with a filler object. if (new_obj_is_tenured) { if (!_old_lab.unallocate_object(new_obj)) { - // The promotion lab failed to unallocate the object. - // We need to overwrite the object with a filler that - // contains no interior pointers. - MemRegion mr((HeapWord*)new_obj, new_obj_size); - // Clean this up and move to oopFactory (see bug 4718422) - SharedHeap::fill_region_with_object(mr); + CollectedHeap::fill_with_object((HeapWord*) new_obj, new_obj_size); } - } else { - if (!_young_lab.unallocate_object(new_obj)) { - // The promotion lab failed to unallocate the object. - // We need to overwrite the object with a filler that - // contains no interior pointers. - MemRegion mr((HeapWord*)new_obj, new_obj_size); - // Clean this up and move to oopFactory (see bug 4718422) - SharedHeap::fill_region_with_object(mr); - } + } else if (!_young_lab.unallocate_object(new_obj)) { + CollectedHeap::fill_with_object((HeapWord*) new_obj, new_obj_size); } // don't update this before the unallocation! diff -r 06d2c3204df4 -r ffe19141e312 src/share/vm/gc_implementation/shared/mutableNUMASpace.cpp --- a/src/share/vm/gc_implementation/shared/mutableNUMASpace.cpp Fri Dec 12 10:19:39 2008 -0800 +++ b/src/share/vm/gc_implementation/shared/mutableNUMASpace.cpp Fri Dec 12 15:37:46 2008 -0800 @@ -76,8 +76,8 @@ MutableSpace *s = ls->space(); if (s->top() < top()) { // For all spaces preceeding the one containing top() if (s->free_in_words() > 0) { - SharedHeap::fill_region_with_object(MemRegion(s->top(), s->end())); size_t area_touched_words = pointer_delta(s->end(), s->top()); + CollectedHeap::fill_with_object(s->top(), area_touched_words); #ifndef ASSERT if (!ZapUnusedHeapArea) { area_touched_words = MIN2((size_t)align_object_size(typeArrayOopDesc::header_size(T_INT)), @@ -686,11 +686,11 @@ // a minimal object; assuming that's not the last chunk in which case we don't care. if (i < lgrp_spaces()->length() - 1) { size_t remainder = pointer_delta(s->end(), value); - const size_t minimal_object_size = oopDesc::header_size(); - if (remainder < minimal_object_size && remainder > 0) { - // Add a filler object of a minimal size, it will cross the chunk boundary. - SharedHeap::fill_region_with_object(MemRegion(value, minimal_object_size)); - value += minimal_object_size; + const size_t min_fill_size = CollectedHeap::min_fill_size(); + if (remainder < min_fill_size && remainder > 0) { + // Add a minimum size filler object; it will cross the chunk boundary. + CollectedHeap::fill_with_object(value, min_fill_size); + value += min_fill_size; assert(!s->contains(value), "Should be in the next chunk"); // Restart the loop from the same chunk, since the value has moved // to the next one. diff -r 06d2c3204df4 -r ffe19141e312 src/share/vm/gc_interface/collectedHeap.cpp --- a/src/share/vm/gc_interface/collectedHeap.cpp Fri Dec 12 10:19:39 2008 -0800 +++ b/src/share/vm/gc_interface/collectedHeap.cpp Fri Dec 12 15:37:46 2008 -0800 @@ -30,12 +30,21 @@ int CollectedHeap::_fire_out_of_memory_count = 0; #endif +size_t CollectedHeap::_filler_array_max_size = 0; + // Memory state functions. -CollectedHeap::CollectedHeap() : - _reserved(), _barrier_set(NULL), _is_gc_active(false), - _total_collections(0), _total_full_collections(0), - _gc_cause(GCCause::_no_gc), _gc_lastcause(GCCause::_no_gc) { +CollectedHeap::CollectedHeap() +{ + const size_t max_len = size_t(arrayOopDesc::max_array_length(T_INT)); + const size_t elements_per_word = HeapWordSize / sizeof(jint); + _filler_array_max_size = align_object_size(filler_array_hdr_size() + + max_len * elements_per_word); + + _barrier_set = NULL; + _is_gc_active = false; + _total_collections = _total_full_collections = 0; + _gc_cause = _gc_lastcause = GCCause::_no_gc; NOT_PRODUCT(_promotion_failure_alot_count = 0;) NOT_PRODUCT(_promotion_failure_alot_gc_number = 0;) @@ -128,6 +137,95 @@ return obj; } +size_t CollectedHeap::filler_array_hdr_size() { + return size_t(arrayOopDesc::header_size(T_INT)); +} + +size_t CollectedHeap::filler_array_min_size() { + return align_object_size(filler_array_hdr_size()); +} + +size_t CollectedHeap::filler_array_max_size() { + return _filler_array_max_size; +} + +#ifdef ASSERT +void CollectedHeap::fill_args_check(HeapWord* start, size_t words) +{ + assert(words >= min_fill_size(), "too small to fill"); + assert(words % MinObjAlignment == 0, "unaligned size"); + assert(Universe::heap()->is_in_reserved(start), "not in heap"); + assert(Universe::heap()->is_in_reserved(start + words - 1), "not in heap"); +} + +void CollectedHeap::zap_filler_array(HeapWord* start, size_t words) +{ + if (ZapFillerObjects) { + Copy::fill_to_words(start + filler_array_hdr_size(), + words - filler_array_hdr_size(), 0XDEAFBABE); + } +} +#endif // ASSERT + +void +CollectedHeap::fill_with_array(HeapWord* start, size_t words) +{ + assert(words >= filler_array_min_size(), "too small for an array"); + assert(words <= filler_array_max_size(), "too big for a single object"); + + const size_t payload_size = words - filler_array_hdr_size(); + const size_t len = payload_size * HeapWordSize / sizeof(jint); + + // Set the length first for concurrent GC. + ((arrayOop)start)->set_length((int)len); + post_allocation_setup_common(Universe::fillerArrayKlassObj(), start, + words); + DEBUG_ONLY(zap_filler_array(start, words);) +} + +void +CollectedHeap::fill_with_object_impl(HeapWord* start, size_t words) +{ + assert(words <= filler_array_max_size(), "too big for a single object"); + + if (words >= filler_array_min_size()) { + fill_with_array(start, words); + } else if (words > 0) { + assert(words == min_fill_size(), "unaligned size"); + post_allocation_setup_common(SystemDictionary::object_klass(), start, + words); + } +} + +void CollectedHeap::fill_with_object(HeapWord* start, size_t words) +{ + DEBUG_ONLY(fill_args_check(start, words);) + HandleMark hm; // Free handles before leaving. + fill_with_object_impl(start, words); +} + +void CollectedHeap::fill_with_objects(HeapWord* start, size_t words) +{ + DEBUG_ONLY(fill_args_check(start, words);) + HandleMark hm; // Free handles before leaving. + +#ifdef LP64 + // A single array can fill ~8G, so multiple objects are needed only in 64-bit. + // First fill with arrays, ensuring that any remaining space is big enough to + // fill. The remainder is filled with a single object. + const size_t min = min_fill_size(); + const size_t max = filler_array_max_size(); + while (words > max) { + const size_t cur = words - max >= min ? max : max - min; + fill_with_array(start, cur); + start += cur; + words -= cur; + } +#endif + + fill_with_object_impl(start, words); +} + oop CollectedHeap::new_store_barrier(oop new_obj) { // %%% This needs refactoring. (It was imported from the server compiler.) guarantee(can_elide_tlab_store_barriers(), "store barrier elision not supported"); diff -r 06d2c3204df4 -r ffe19141e312 src/share/vm/gc_interface/collectedHeap.hpp --- a/src/share/vm/gc_interface/collectedHeap.hpp Fri Dec 12 10:19:39 2008 -0800 +++ b/src/share/vm/gc_interface/collectedHeap.hpp Fri Dec 12 15:37:46 2008 -0800 @@ -47,6 +47,9 @@ static int _fire_out_of_memory_count; #endif + // Used for filler objects (static, but initialized in ctor). + static size_t _filler_array_max_size; + protected: MemRegion _reserved; BarrierSet* _barrier_set; @@ -119,6 +122,21 @@ // Clears an allocated object. inline static void init_obj(HeapWord* obj, size_t size); + // Filler object utilities. + static inline size_t filler_array_hdr_size(); + static inline size_t filler_array_min_size(); + static inline size_t filler_array_max_size(); + + DEBUG_ONLY(static void fill_args_check(HeapWord* start, size_t words);) + DEBUG_ONLY(static void zap_filler_array(HeapWord* start, size_t words);) + + // Fill with a single array; caller must ensure filler_array_min_size() <= + // words <= filler_array_max_size(). + static inline void fill_with_array(HeapWord* start, size_t words); + + // Fill with a single object (either an int array or a java.lang.Object). + static inline void fill_with_object_impl(HeapWord* start, size_t words); + // Verification functions virtual void check_for_bad_heap_word_value(HeapWord* addr, size_t size) PRODUCT_RETURN; @@ -294,6 +312,27 @@ // The boundary between a "large" and "small" array of primitives, in words. virtual size_t large_typearray_limit() = 0; + // Utilities for turning raw memory into filler objects. + // + // min_fill_size() is the smallest region that can be filled. + // fill_with_objects() can fill arbitrary-sized regions of the heap using + // multiple objects. fill_with_object() is for regions known to be smaller + // than the largest array of integers; it uses a single object to fill the + // region and has slightly less overhead. + static size_t min_fill_size() { + return size_t(align_object_size(oopDesc::header_size())); + } + + static void fill_with_objects(HeapWord* start, size_t words); + + static void fill_with_object(HeapWord* start, size_t words); + static void fill_with_object(MemRegion region) { + fill_with_object(region.start(), region.word_size()); + } + static void fill_with_object(HeapWord* start, HeapWord* end) { + fill_with_object(start, pointer_delta(end, start)); + } + // Some heaps may offer a contiguous region for shared non-blocking // allocation, via inlined code (by exporting the address of the top and // end fields defining the extent of the contiguous allocation region.) diff -r 06d2c3204df4 -r ffe19141e312 src/share/vm/gc_interface/collectedHeap.inline.hpp --- a/src/share/vm/gc_interface/collectedHeap.inline.hpp Fri Dec 12 10:19:39 2008 -0800 +++ b/src/share/vm/gc_interface/collectedHeap.inline.hpp Fri Dec 12 15:37:46 2008 -0800 @@ -34,7 +34,6 @@ void CollectedHeap::post_allocation_setup_no_klass_install(KlassHandle klass, HeapWord* objPtr, size_t size) { - oop obj = (oop)objPtr; assert(obj != NULL, "NULL object pointer"); @@ -44,9 +43,6 @@ // May be bootstrapping obj->set_mark(markOopDesc::prototype()); } - - // support low memory notifications (no-op if not enabled) - LowMemoryDetector::detect_low_memory_for_collected_pools(); } void CollectedHeap::post_allocation_install_obj_klass(KlassHandle klass, @@ -65,6 +61,9 @@ // Support for jvmti and dtrace inline void post_allocation_notify(KlassHandle klass, oop obj) { + // support low memory notifications (no-op if not enabled) + LowMemoryDetector::detect_low_memory_for_collected_pools(); + // support for JVMTI VMObjectAlloc event (no-op if not enabled) JvmtiExport::vm_object_alloc_event_collector(obj); diff -r 06d2c3204df4 -r ffe19141e312 src/share/vm/includeDB_gc --- a/src/share/vm/includeDB_gc Fri Dec 12 10:19:39 2008 -0800 +++ b/src/share/vm/includeDB_gc Fri Dec 12 15:37:46 2008 -0800 @@ -28,21 +28,22 @@ collectedHeap.cpp collectedHeap.inline.hpp collectedHeap.cpp init.hpp collectedHeap.cpp oop.inline.hpp +collectedHeap.cpp systemDictionary.hpp collectedHeap.cpp thread_.inline.hpp collectedHeap.hpp allocation.hpp collectedHeap.hpp barrierSet.hpp collectedHeap.hpp gcCause.hpp collectedHeap.hpp handles.hpp -collectedHeap.hpp perfData.hpp +collectedHeap.hpp perfData.hpp collectedHeap.hpp safepoint.hpp collectedHeap.inline.hpp arrayOop.hpp collectedHeap.inline.hpp collectedHeap.hpp collectedHeap.inline.hpp copy.hpp collectedHeap.inline.hpp jvmtiExport.hpp -collectedHeap.inline.hpp lowMemoryDetector.hpp -collectedHeap.inline.hpp sharedRuntime.hpp +collectedHeap.inline.hpp lowMemoryDetector.hpp +collectedHeap.inline.hpp sharedRuntime.hpp collectedHeap.inline.hpp thread.hpp collectedHeap.inline.hpp threadLocalAllocBuffer.inline.hpp collectedHeap.inline.hpp universe.hpp diff -r 06d2c3204df4 -r ffe19141e312 src/share/vm/memory/permGen.cpp --- a/src/share/vm/memory/permGen.cpp Fri Dec 12 10:19:39 2008 -0800 +++ b/src/share/vm/memory/permGen.cpp Fri Dec 12 15:37:46 2008 -0800 @@ -26,20 +26,24 @@ #include "incls/_permGen.cpp.incl" HeapWord* PermGen::mem_allocate_in_gen(size_t size, Generation* gen) { - MutexLocker ml(Heap_lock); GCCause::Cause next_cause = GCCause::_permanent_generation_full; GCCause::Cause prev_cause = GCCause::_no_gc; + unsigned int gc_count_before, full_gc_count_before; + HeapWord* obj; for (;;) { - HeapWord* obj = gen->allocate(size, false); - if (obj != NULL) { - return obj; - } - if (gen->capacity() < _capacity_expansion_limit || - prev_cause != GCCause::_no_gc) { - obj = gen->expand_and_allocate(size, false); - } - if (obj == NULL && prev_cause != GCCause::_last_ditch_collection) { + { + MutexLocker ml(Heap_lock); + if ((obj = gen->allocate(size, false)) != NULL) { + return obj; + } + if (gen->capacity() < _capacity_expansion_limit || + prev_cause != GCCause::_no_gc) { + obj = gen->expand_and_allocate(size, false); + } + if (obj != NULL || prev_cause == GCCause::_last_ditch_collection) { + return obj; + } if (GC_locker::is_active_and_needs_gc()) { // If this thread is not in a jni critical section, we stall // the requestor until the critical section has cleared and @@ -61,31 +65,27 @@ return NULL; } } + // Read the GC count while holding the Heap_lock + gc_count_before = SharedHeap::heap()->total_collections(); + full_gc_count_before = SharedHeap::heap()->total_full_collections(); + } - // Read the GC count while holding the Heap_lock - unsigned int gc_count_before = SharedHeap::heap()->total_collections(); - unsigned int full_gc_count_before = SharedHeap::heap()->total_full_collections(); - { - MutexUnlocker mu(Heap_lock); // give up heap lock, execute gets it back - VM_GenCollectForPermanentAllocation op(size, gc_count_before, full_gc_count_before, - next_cause); - VMThread::execute(&op); - if (!op.prologue_succeeded() || op.gc_locked()) { - assert(op.result() == NULL, "must be NULL if gc_locked() is true"); - continue; // retry and/or stall as necessary - } - obj = op.result(); - assert(obj == NULL || SharedHeap::heap()->is_in_reserved(obj), - "result not in heap"); - if (obj != NULL) { - return obj; - } - } - prev_cause = next_cause; - next_cause = GCCause::_last_ditch_collection; - } else { + // Give up heap lock above, VMThread::execute below gets it back + VM_GenCollectForPermanentAllocation op(size, gc_count_before, full_gc_count_before, + next_cause); + VMThread::execute(&op); + if (!op.prologue_succeeded() || op.gc_locked()) { + assert(op.result() == NULL, "must be NULL if gc_locked() is true"); + continue; // retry and/or stall as necessary + } + obj = op.result(); + assert(obj == NULL || SharedHeap::heap()->is_in_reserved(obj), + "result not in heap"); + if (obj != NULL) { return obj; } + prev_cause = next_cause; + next_cause = GCCause::_last_ditch_collection; } } diff -r 06d2c3204df4 -r ffe19141e312 src/share/vm/memory/sharedHeap.cpp --- a/src/share/vm/memory/sharedHeap.cpp Fri Dec 12 10:19:39 2008 -0800 +++ b/src/share/vm/memory/sharedHeap.cpp Fri Dec 12 15:37:46 2008 -0800 @@ -248,46 +248,6 @@ perm_gen()->ref_processor_init(); } -void SharedHeap::fill_region_with_object(MemRegion mr) { - // Disable the posting of JVMTI VMObjectAlloc events as we - // don't want the filling of tlabs with filler arrays to be - // reported to the profiler. - NoJvmtiVMObjectAllocMark njm; - - // Disable low memory detector because there is no real allocation. - LowMemoryDetectorDisabler lmd_dis; - - // It turns out that post_allocation_setup_array takes a handle, so the - // call below contains an implicit conversion. Best to free that handle - // as soon as possible. - HandleMark hm; - - size_t word_size = mr.word_size(); - size_t aligned_array_header_size = - align_object_size(typeArrayOopDesc::header_size(T_INT)); - - if (word_size >= aligned_array_header_size) { - const size_t array_length = - pointer_delta(mr.end(), mr.start()) - - typeArrayOopDesc::header_size(T_INT); - const size_t array_length_words = - array_length * (HeapWordSize/sizeof(jint)); - post_allocation_setup_array(Universe::intArrayKlassObj(), - mr.start(), - mr.word_size(), - (int)array_length_words); -#ifdef ASSERT - HeapWord* elt_words = (mr.start() + typeArrayOopDesc::header_size(T_INT)); - Copy::fill_to_words(elt_words, array_length, 0xDEAFBABE); -#endif - } else { - assert(word_size == (size_t)oopDesc::header_size(), "Unaligned?"); - post_allocation_setup_obj(SystemDictionary::object_klass(), - mr.start(), - mr.word_size()); - } -} - // Some utilities. void SharedHeap::print_size_transition(outputStream* out, size_t bytes_before, diff -r 06d2c3204df4 -r ffe19141e312 src/share/vm/memory/sharedHeap.hpp --- a/src/share/vm/memory/sharedHeap.hpp Fri Dec 12 10:19:39 2008 -0800 +++ b/src/share/vm/memory/sharedHeap.hpp Fri Dec 12 15:37:46 2008 -0800 @@ -108,14 +108,6 @@ void set_perm(PermGen* perm_gen) { _perm_gen = perm_gen; } - // A helper function that fills a region of the heap with - // with a single object. - static void fill_region_with_object(MemRegion mr); - - // Minimum garbage fill object size - static size_t min_fill_size() { return (size_t)align_object_size(oopDesc::header_size()); } - static size_t min_fill_size_in_bytes() { return min_fill_size() * HeapWordSize; } - // This function returns the "GenRemSet" object that allows us to scan // generations; at least the perm gen, possibly more in a fully // generational heap. diff -r 06d2c3204df4 -r ffe19141e312 src/share/vm/memory/space.cpp --- a/src/share/vm/memory/space.cpp Fri Dec 12 10:19:39 2008 -0800 +++ b/src/share/vm/memory/space.cpp Fri Dec 12 15:37:46 2008 -0800 @@ -409,19 +409,9 @@ HeapWord* q, size_t deadlength) { if (allowed_deadspace_words >= deadlength) { allowed_deadspace_words -= deadlength; - oop(q)->set_mark(markOopDesc::prototype()->set_marked()); - const size_t min_int_array_size = typeArrayOopDesc::header_size(T_INT); - if (deadlength >= min_int_array_size) { - oop(q)->set_klass(Universe::intArrayKlassObj()); - typeArrayOop(q)->set_length((int)((deadlength - min_int_array_size) - * (HeapWordSize/sizeof(jint)))); - } else { - assert((int) deadlength == instanceOopDesc::header_size(), - "size for smallest fake dead object doesn't match"); - oop(q)->set_klass(SystemDictionary::object_klass()); - } - assert((int) deadlength == oop(q)->size(), - "make sure size for fake dead object match"); + CollectedHeap::fill_with_object(q, deadlength); + oop(q)->set_mark(oop(q)->mark()->set_marked()); + assert((int) deadlength == oop(q)->size(), "bad filler object size"); // Recall that we required "q == compaction_top". return true; } else { diff -r 06d2c3204df4 -r ffe19141e312 src/share/vm/memory/tenuredGeneration.cpp --- a/src/share/vm/memory/tenuredGeneration.cpp Fri Dec 12 10:19:39 2008 -0800 +++ b/src/share/vm/memory/tenuredGeneration.cpp Fri Dec 12 15:37:46 2008 -0800 @@ -387,7 +387,7 @@ "should contain whole object"); buf->undo_allocation(obj, word_sz); } else { - SharedHeap::fill_region_with_object(MemRegion(obj, word_sz)); + CollectedHeap::fill_with_object(obj, word_sz); } } diff -r 06d2c3204df4 -r ffe19141e312 src/share/vm/memory/threadLocalAllocBuffer.cpp --- a/src/share/vm/memory/threadLocalAllocBuffer.cpp Fri Dec 12 10:19:39 2008 -0800 +++ b/src/share/vm/memory/threadLocalAllocBuffer.cpp Fri Dec 12 15:37:46 2008 -0800 @@ -100,8 +100,7 @@ void ThreadLocalAllocBuffer::make_parsable(bool retire) { if (end() != NULL) { invariants(); - MemRegion mr(top(), hard_end()); - SharedHeap::fill_region_with_object(mr); + CollectedHeap::fill_with_object(top(), hard_end()); if (retire || ZeroTLAB) { // "Reset" the TLAB set_start(NULL); diff -r 06d2c3204df4 -r ffe19141e312 src/share/vm/memory/universe.cpp --- a/src/share/vm/memory/universe.cpp Fri Dec 12 10:19:39 2008 -0800 +++ b/src/share/vm/memory/universe.cpp Fri Dec 12 15:37:46 2008 -0800 @@ -49,16 +49,17 @@ klassOop Universe::_constantPoolCacheKlassObj = NULL; klassOop Universe::_compiledICHolderKlassObj = NULL; klassOop Universe::_systemObjArrayKlassObj = NULL; -oop Universe::_int_mirror = NULL; -oop Universe::_float_mirror = NULL; -oop Universe::_double_mirror = NULL; -oop Universe::_byte_mirror = NULL; -oop Universe::_bool_mirror = NULL; -oop Universe::_char_mirror = NULL; -oop Universe::_long_mirror = NULL; -oop Universe::_short_mirror = NULL; -oop Universe::_void_mirror = NULL; -oop Universe::_mirrors[T_VOID+1] = { NULL /*, NULL...*/ }; +klassOop Universe::_fillerArrayKlassObj = NULL; +oop Universe::_int_mirror = NULL; +oop Universe::_float_mirror = NULL; +oop Universe::_double_mirror = NULL; +oop Universe::_byte_mirror = NULL; +oop Universe::_bool_mirror = NULL; +oop Universe::_char_mirror = NULL; +oop Universe::_long_mirror = NULL; +oop Universe::_short_mirror = NULL; +oop Universe::_void_mirror = NULL; +oop Universe::_mirrors[T_VOID+1] = { NULL /*, NULL...*/ }; oop Universe::_main_thread_group = NULL; oop Universe::_system_thread_group = NULL; typeArrayOop Universe::_the_empty_byte_array = NULL; @@ -126,6 +127,7 @@ f(instanceKlassKlassObj()); f(constantPoolKlassObj()); f(systemObjArrayKlassObj()); + f(fillerArrayKlassObj()); } void Universe::oops_do(OopClosure* f, bool do_all) { @@ -180,6 +182,7 @@ f->do_oop((oop*)&_constantPoolCacheKlassObj); f->do_oop((oop*)&_compiledICHolderKlassObj); f->do_oop((oop*)&_systemObjArrayKlassObj); + f->do_oop((oop*)&_fillerArrayKlassObj); f->do_oop((oop*)&_the_empty_byte_array); f->do_oop((oop*)&_the_empty_short_array); f->do_oop((oop*)&_the_empty_int_array); @@ -257,16 +260,17 @@ _typeArrayKlassObjs[T_INT] = _intArrayKlassObj; _typeArrayKlassObjs[T_LONG] = _longArrayKlassObj; - _methodKlassObj = methodKlass::create_klass(CHECK); - _constMethodKlassObj = constMethodKlass::create_klass(CHECK); - _methodDataKlassObj = methodDataKlass::create_klass(CHECK); + _methodKlassObj = methodKlass::create_klass(CHECK); + _constMethodKlassObj = constMethodKlass::create_klass(CHECK); + _methodDataKlassObj = methodDataKlass::create_klass(CHECK); _constantPoolKlassObj = constantPoolKlass::create_klass(CHECK); _constantPoolCacheKlassObj = constantPoolCacheKlass::create_klass(CHECK); _compiledICHolderKlassObj = compiledICHolderKlass::create_klass(CHECK); _systemObjArrayKlassObj = objArrayKlassKlass::cast(objArrayKlassKlassObj())->allocate_system_objArray_klass(CHECK); + _fillerArrayKlassObj = typeArrayKlass::create_klass(T_INT, sizeof(jint), "", CHECK); - _the_empty_byte_array = oopFactory::new_permanent_byteArray(0, CHECK); + _the_empty_byte_array = oopFactory::new_permanent_byteArray(0, CHECK); _the_empty_short_array = oopFactory::new_permanent_shortArray(0, CHECK); _the_empty_int_array = oopFactory::new_permanent_intArray(0, CHECK); _the_empty_system_obj_array = oopFactory::new_system_objArray(0, CHECK); @@ -274,7 +278,6 @@ _the_array_interfaces_array = oopFactory::new_system_objArray(2, CHECK); _vm_exception = oopFactory::new_symbol("vm exception holder", CHECK); } else { - FileMapInfo *mapinfo = FileMapInfo::current_info(); char* buffer = mapinfo->region_base(CompactingPermGenGen::md); void** vtbl_list = (void**)buffer; diff -r 06d2c3204df4 -r ffe19141e312 src/share/vm/memory/universe.hpp --- a/src/share/vm/memory/universe.hpp Fri Dec 12 10:19:39 2008 -0800 +++ b/src/share/vm/memory/universe.hpp Fri Dec 12 15:37:46 2008 -0800 @@ -92,6 +92,7 @@ class Universe: AllStatic { + // Ugh. Universe is much too friendly. friend class MarkSweep; friend class oopDesc; friend class ClassLoader; @@ -132,6 +133,7 @@ static klassOop _constantPoolCacheKlassObj; static klassOop _compiledICHolderKlassObj; static klassOop _systemObjArrayKlassObj; + static klassOop _fillerArrayKlassObj; // Known objects in the VM @@ -264,6 +266,7 @@ static klassOop constantPoolCacheKlassObj() { return _constantPoolCacheKlassObj; } static klassOop compiledICHolderKlassObj() { return _compiledICHolderKlassObj; } static klassOop systemObjArrayKlassObj() { return _systemObjArrayKlassObj; } + static klassOop fillerArrayKlassObj() { return _fillerArrayKlassObj; } // Known objects in tbe VM static oop int_mirror() { return check_mirror(_int_mirror); diff -r 06d2c3204df4 -r ffe19141e312 src/share/vm/oops/arrayOop.hpp --- a/src/share/vm/oops/arrayOop.hpp Fri Dec 12 10:19:39 2008 -0800 +++ b/src/share/vm/oops/arrayOop.hpp Fri Dec 12 15:37:46 2008 -0800 @@ -96,19 +96,20 @@ : typesize_in_bytes/HeapWordSize); } - // This method returns the maximum length that can passed into - // typeArrayOop::object_size(scale, length, header_size) without causing an - // overflow. We substract an extra 2*wordSize to guard against double word - // alignments. It gets the scale from the type2aelembytes array. + // Return the maximum length of an array of BasicType. The length can passed + // to typeArrayOop::object_size(scale, length, header_size) without causing an + // overflow. static int32_t max_array_length(BasicType type) { assert(type >= 0 && type < T_CONFLICT, "wrong type"); assert(type2aelembytes(type) != 0, "wrong type"); - // We use max_jint, since object_size is internally represented by an 'int' - // This gives us an upper bound of max_jint words for the size of the oop. - int32_t max_words = (max_jint - header_size(type) - 2); - int elembytes = type2aelembytes(type); - jlong len = ((jlong)max_words * HeapWordSize) / elembytes; - return (len > max_jint) ? max_jint : (int32_t)len; + const int bytes_per_element = type2aelembytes(type); + if (bytes_per_element < HeapWordSize) { + return max_jint; + } + + const int32_t max_words = align_size_down(max_jint, MinObjAlignment); + const int32_t max_element_words = max_words - header_size(type); + const int32_t words_per_element = bytes_per_element >> LogHeapWordSize; + return max_element_words / words_per_element; } - }; diff -r 06d2c3204df4 -r ffe19141e312 src/share/vm/oops/typeArrayKlass.cpp --- a/src/share/vm/oops/typeArrayKlass.cpp Fri Dec 12 10:19:39 2008 -0800 +++ b/src/share/vm/oops/typeArrayKlass.cpp Fri Dec 12 15:37:46 2008 -0800 @@ -36,13 +36,14 @@ return element_type() == tak->element_type(); } -klassOop typeArrayKlass::create_klass(BasicType type, int scale, TRAPS) { +klassOop typeArrayKlass::create_klass(BasicType type, int scale, + const char* name_str, TRAPS) { typeArrayKlass o; symbolHandle sym(symbolOop(NULL)); // bootstrapping: don't create sym if symbolKlass not created yet - if (Universe::symbolKlassObj() != NULL) { - sym = oopFactory::new_symbol_handle(external_name(type), CHECK_NULL); + if (Universe::symbolKlassObj() != NULL && name_str != NULL) { + sym = oopFactory::new_symbol_handle(name_str, CHECK_NULL); } KlassHandle klassklass (THREAD, Universe::typeArrayKlassKlassObj()); diff -r 06d2c3204df4 -r ffe19141e312 src/share/vm/oops/typeArrayKlass.hpp --- a/src/share/vm/oops/typeArrayKlass.hpp Fri Dec 12 10:19:39 2008 -0800 +++ b/src/share/vm/oops/typeArrayKlass.hpp Fri Dec 12 15:37:46 2008 -0800 @@ -39,7 +39,11 @@ // klass allocation DEFINE_ALLOCATE_PERMANENT(typeArrayKlass); - static klassOop create_klass(BasicType type, int scale, TRAPS); + static klassOop create_klass(BasicType type, int scale, const char* name_str, + TRAPS); + static inline klassOop create_klass(BasicType type, int scale, TRAPS) { + return create_klass(type, scale, external_name(type), CHECK_NULL); + } int oop_size(oop obj) const; int klass_oop_size() const { return object_size(); } diff -r 06d2c3204df4 -r ffe19141e312 src/share/vm/runtime/arguments.cpp --- a/src/share/vm/runtime/arguments.cpp Fri Dec 12 10:19:39 2008 -0800 +++ b/src/share/vm/runtime/arguments.cpp Fri Dec 12 15:37:46 2008 -0800 @@ -1517,6 +1517,16 @@ MarkSweepAlwaysCompactCount = 1; // Move objects every gc. } + if (UseParallelOldGC && ParallelOldGCSplitALot) { + // Settings to encourage splitting. + if (!FLAG_IS_CMDLINE(NewRatio)) { + FLAG_SET_CMDLINE(intx, NewRatio, 2); + } + if (!FLAG_IS_CMDLINE(ScavengeBeforeFullGC)) { + FLAG_SET_CMDLINE(bool, ScavengeBeforeFullGC, false); + } + } + status = status && verify_percentage(GCHeapFreeLimit, "GCHeapFreeLimit"); status = status && verify_percentage(GCTimeLimit, "GCTimeLimit"); if (GCTimeLimit == 100) { diff -r 06d2c3204df4 -r ffe19141e312 src/share/vm/runtime/globals.hpp --- a/src/share/vm/runtime/globals.hpp Fri Dec 12 10:19:39 2008 -0800 +++ b/src/share/vm/runtime/globals.hpp Fri Dec 12 15:37:46 2008 -0800 @@ -625,6 +625,9 @@ develop(bool, CheckZapUnusedHeapArea, false, \ "Check zapping of unused heap space") \ \ + develop(bool, ZapFillerObjects, trueInDebug, \ + "Zap filler objects with 0xDEAFBABE") \ + \ develop(bool, PrintVMMessages, true, \ "Print vm messages on console") \ \ @@ -1200,11 +1203,12 @@ product(uintx, ParallelCMSThreads, 0, \ "Max number of threads CMS will use for concurrent work") \ \ - develop(bool, ParallelOldMTUnsafeMarkBitMap, false, \ - "Use the Parallel Old MT unsafe in marking the bitmap") \ - \ - develop(bool, ParallelOldMTUnsafeUpdateLiveData, false, \ - "Use the Parallel Old MT unsafe in update of live size") \ + develop(bool, ParallelOldGCSplitALot, false, \ + "Provoke splitting (copying data from a young gen space to" \ + "multiple destination spaces)") \ + \ + develop(uintx, ParallelOldGCSplitInterval, 3, \ + "How often to provoke splitting a young gen space") \ \ develop(bool, TraceRegionTasksQueuing, false, \ "Trace the queuing of the region tasks") \