# HG changeset patch # Parent 509077a061147666232faf05684e7115fd6fc2ed # User tschatzl diff -r 509077a06114 src/share/vm/gc/g1/g1AllocRegion.cpp --- a/src/share/vm/gc/g1/g1AllocRegion.cpp Fri Nov 20 19:17:57 2015 +0000 +++ b/src/share/vm/gc/g1/g1AllocRegion.cpp Tue Nov 24 10:17:21 2015 +0100 @@ -26,6 +26,7 @@ #include "gc/g1/g1AllocRegion.inline.hpp" #include "gc/g1/g1EvacStats.inline.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp" +#include "gc/g1/retainedRegionsList.hpp" #include "runtime/orderAccess.inline.hpp" G1CollectedHeap* G1AllocRegion::_g1h = NULL; @@ -91,13 +92,12 @@ return result; } -size_t G1AllocRegion::retire(bool fill_up) { - assert_alloc_region(_alloc_region != NULL, "not initialized properly"); +size_t G1AllocRegion::retire_region(HeapRegion* alloc_region, bool fill_up) { + assert_alloc_region(alloc_region != NULL, "Cannot retire NULL region"); size_t result = 0; - trace("retiring"); - HeapRegion* alloc_region = _alloc_region; + trace("retiring", alloc_region); if (alloc_region != _dummy_region) { // We never have to check whether the active region is empty or not, // and potentially free it if it is, given that it's guaranteed that @@ -108,30 +108,50 @@ if (fill_up) { result = fill_up_remaining_space(alloc_region, _bot_updates); } + size_t const allocated_bytes = alloc_region->recently_allocated_bytes(); - assert_alloc_region(alloc_region->used() >= _used_bytes_before, "invariant"); - size_t allocated_bytes = alloc_region->used() - _used_bytes_before; - retire_region(alloc_region, allocated_bytes); - _used_bytes_before = 0; - _alloc_region = _dummy_region; + assert(alloc_region->used() >= allocated_bytes, "invariant"); + alloc_region->reset_after_alloc(); + notify_region_retired(alloc_region, allocated_bytes); } - trace("retired"); + trace("retired", alloc_region); return result; } -HeapWord* G1AllocRegion::new_alloc_region_and_allocate(size_t word_size, - bool force) { +HeapRegion* G1AllocRegion::new_alloc_region_and_allocate_helper(size_t min_word_size, + size_t desired_word_size, + bool force, + HeapWord** result, + size_t* actual_word_size) { + HeapRegion* r = allocate_new_region(desired_word_size, force); + if (r != NULL) { + *result = allocate(r, desired_word_size, _bot_updates); + guarantee(*result != NULL, "New region must have had at least " SIZE_FORMAT " space", desired_word_size); + *actual_word_size = desired_word_size; + } + return r; +} + +HeapWord* G1AllocRegion::new_alloc_region_and_allocate(size_t min_word_size, + size_t desired_word_size, + bool force, + size_t* actual_word_size) { + release_or_retain_during_gc(); + assert_alloc_region(_alloc_region == _dummy_region, "pre-condition"); - assert_alloc_region(_used_bytes_before == 0, "pre-condition"); trace("attempting region allocation"); - HeapRegion* new_alloc_region = allocate_new_region(word_size, force); + HeapWord* result = NULL; + HeapRegion* new_alloc_region = new_alloc_region_and_allocate_helper(min_word_size, + desired_word_size, + force, + &result, + actual_word_size); if (new_alloc_region != NULL) { new_alloc_region->reset_pre_dummy_top(); - // Need to do this before the allocation - _used_bytes_before = new_alloc_region->used(); - HeapWord* result = allocate(new_alloc_region, word_size, _bot_updates); + // Update allocation start. + new_alloc_region->update_top_at_alloc_start(result); assert_alloc_region(result != NULL, "the allocation should succeeded"); OrderAccess::storestore(); @@ -150,30 +170,29 @@ void G1AllocRegion::init() { trace("initializing"); - assert_alloc_region(_alloc_region == NULL && _used_bytes_before == 0, "pre-condition"); + assert(_alloc_region == NULL, "pre-condition"); assert_alloc_region(_dummy_region != NULL, "should have been set"); - _alloc_region = _dummy_region; + reset_alloc_region(); _count = 0; trace("initialized"); } void G1AllocRegion::set(HeapRegion* alloc_region) { - trace("setting"); + trace("setting", alloc_region); // We explicitly check that the region is not empty to make sure we // maintain the "the alloc region cannot be empty" invariant. - assert_alloc_region(alloc_region != NULL && !alloc_region->is_empty(), "pre-condition"); - assert_alloc_region(_alloc_region == _dummy_region && - _used_bytes_before == 0 && _count == 0, - "pre-condition"); - - _used_bytes_before = alloc_region->used(); - _alloc_region = alloc_region; - _count += 1; - trace("set"); + assert(alloc_region != NULL && !alloc_region->is_empty(), "pre-condition"); + assert(_alloc_region == _dummy_region && _count == 0, "pre-condition"); + + alloc_region->update_top_at_alloc_start(alloc_region->top()); + + _alloc_region = alloc_region; + _count += 1; + trace("set", alloc_region); } void G1AllocRegion::update_alloc_region(HeapRegion* alloc_region) { - trace("update"); + trace("update", alloc_region); // We explicitly check that the region is not empty to make sure we // maintain the "the alloc region cannot be empty" invariant. assert_alloc_region(alloc_region != NULL && !alloc_region->is_empty(), "pre-condition"); @@ -181,21 +200,32 @@ _alloc_region = alloc_region; _alloc_region->set_allocation_context(allocation_context()); _count += 1; - trace("updated"); + trace("updated", alloc_region); +} + +void G1AllocRegion::release_or_retain_during_gc() { + trace("releasing during gc", _alloc_region); + retire_region(_alloc_region, true); + reset_alloc_region(); + + assert(_alloc_region == _dummy_region, "post-condition of release_during_gc()"); + trace("released during gc"); } HeapRegion* G1AllocRegion::release() { trace("releasing"); HeapRegion* alloc_region = _alloc_region; - retire(false /* fill_up */); - assert_alloc_region(_alloc_region == _dummy_region, "post-condition of retire()"); + retire_region(alloc_region, false); + reset_alloc_region(); + + assert(_alloc_region == _dummy_region, "post-condition of release()"); _alloc_region = NULL; trace("released"); return (alloc_region == _dummy_region) ? NULL : alloc_region; } #if G1_ALLOC_REGION_TRACING -void G1AllocRegion::trace(const char* str, size_t min_word_size, size_t desired_word_size, size_t actual_word_size, HeapWord* result) { +void G1AllocRegion::trace(const char* str, HeapRegion* r, size_t min_word_size, size_t desired_word_size, size_t actual_word_size, HeapWord* result) { // All the calls to trace that set either just the size or the size // and the result are considered part of level 2 tracing and are // skipped during level 1 tracing. @@ -204,7 +234,7 @@ char hr_buffer[buffer_length]; char rest_buffer[buffer_length]; - HeapRegion* alloc_region = _alloc_region; + HeapRegion* alloc_region = r; if (alloc_region == NULL) { jio_snprintf(hr_buffer, buffer_length, "NULL"); } else if (alloc_region == _dummy_region) { @@ -236,36 +266,74 @@ G1AllocRegion::G1AllocRegion(const char* name, bool bot_updates) : _name(name), _bot_updates(bot_updates), - _alloc_region(NULL), _count(0), _used_bytes_before(0), - _allocation_context(AllocationContext::system()) { } - + _alloc_region(NULL), _count(0), _allocation_context(AllocationContext::system()) +{ } HeapRegion* MutatorAllocRegion::allocate_new_region(size_t word_size, bool force) { return _g1h->new_mutator_alloc_region(word_size, force); } -void MutatorAllocRegion::retire_region(HeapRegion* alloc_region, - size_t allocated_bytes) { +void MutatorAllocRegion::notify_region_retired(HeapRegion* alloc_region, + size_t allocated_bytes) { _g1h->retire_mutator_alloc_region(alloc_region, allocated_bytes); } -HeapRegion* G1GCAllocRegion::allocate_new_region(size_t word_size, - bool force) { +HeapRegion* G1GCAllocRegion::new_alloc_region_and_allocate_helper(size_t min_word_size, + size_t desired_word_size, + bool force, + HeapWord** result, + size_t* actual_word_size) { + assert(!force, "force parameter not supported"); + + HeapRegion* new_alloc_region = G1RetainAllocRegionsDuringGC ? + _retained_regions.get_region_and_allocate(min_word_size, + desired_word_size, + result, + actual_word_size) : + NULL; + if (new_alloc_region == NULL) { + // We did not get any retained region. Always allocate full desired size from new + // regions. + new_alloc_region = G1AllocRegion::new_alloc_region_and_allocate_helper(desired_word_size, + desired_word_size, + force, + result, + actual_word_size); + } + + return new_alloc_region; +} + +HeapRegion* G1GCAllocRegion::allocate_new_region(size_t word_size, bool force) { assert(!force, "not supported for GC alloc regions"); return _g1h->new_gc_alloc_region(word_size, count(), _purpose); } -void G1GCAllocRegion::retire_region(HeapRegion* alloc_region, - size_t allocated_bytes) { +void G1GCAllocRegion::notify_region_retired(HeapRegion* alloc_region, size_t allocated_bytes) { _g1h->retire_gc_alloc_region(alloc_region, allocated_bytes, _purpose); } -size_t G1GCAllocRegion::retire(bool fill_up) { - HeapRegion* retired = get(); - size_t end_waste = G1AllocRegion::retire(fill_up); +HeapRegion* G1GCAllocRegion::release() { + _retained_regions.retire_all(); + HeapRegion* result = G1AllocRegion::release(); + if (result != NULL) { + result->reset_after_alloc(); + } + return result; +} + +void G1GCAllocRegion::release_or_retain_during_gc() { + if (get() != NULL) { + _retained_regions.append(_alloc_region); + reset_alloc_region(); + } +} + +size_t G1GCAllocRegion::retire_region(HeapRegion* region, bool fill_up) { + size_t end_waste = G1AllocRegion::retire_region(region, fill_up); // Do not count retirement of the dummy allocation region. - if (retired != NULL) { + if (region != NULL) { _stats->add_region_end_waste(end_waste / HeapWordSize); } return end_waste; @@ -296,5 +364,5 @@ } } } - return G1AllocRegion::release(); + return G1GCAllocRegion::release(); } diff -r 509077a06114 src/share/vm/gc/g1/g1AllocRegion.hpp --- a/src/share/vm/gc/g1/g1AllocRegion.hpp Fri Nov 20 19:17:57 2015 +0000 +++ b/src/share/vm/gc/g1/g1AllocRegion.hpp Tue Nov 24 10:17:21 2015 +0100 @@ -28,6 +28,7 @@ #include "gc/g1/heapRegion.hpp" #include "gc/g1/g1EvacStats.hpp" #include "gc/g1/g1InCSetState.hpp" +#include "gc/g1/retainedRegionsList.hpp" class G1CollectedHeap; @@ -43,7 +44,7 @@ class G1AllocRegion VALUE_OBJ_CLASS_SPEC { -private: +protected: // The active allocating region we are currently allocating out // of. The invariant is that if this object is initialized (i.e., // init() has been called and release() has not) then _alloc_region @@ -56,6 +57,7 @@ // correct use of init() and release()). HeapRegion* volatile _alloc_region; +private: // Allocation context associated with this alloc region. AllocationContext_t _allocation_context; @@ -68,11 +70,6 @@ // distinct regions this object can used during an active interval. uint _count; - // When we set up a new active region we save its used bytes in this - // field so that, when we retire it, we can calculate how much space - // we allocated in it. - size_t _used_bytes_before; - // When true, indicates that allocate calls should do BOT updates. const bool _bot_updates; @@ -97,20 +94,6 @@ size_t word_size, bool bot_updates); - // Perform a MT-safe allocation out of the given region. - static inline HeapWord* par_allocate(HeapRegion* alloc_region, - size_t word_size, - bool bot_updates); - // Perform a MT-safe allocation out of the given region, with the given - // minimum and desired size. Returns the actual size allocated (between - // minimum and desired size) in actual_word_size if the allocation has been - // successful. - static inline HeapWord* par_allocate(HeapRegion* alloc_region, - size_t min_word_size, - size_t desired_word_size, - size_t* actual_word_size, - bool bot_updates); - // Ensure that the region passed as a parameter has been filled up // so that noone else can allocate out of it any more. // Returns the number of bytes that have been wasted by filled up @@ -122,29 +105,49 @@ // method is used to set it as the active alloc_region void update_alloc_region(HeapRegion* alloc_region); - // Allocate a new active region and use it to perform a word_size - // allocation. The force parameter will be passed on to - // G1CollectedHeap::allocate_new_alloc_region() and tells it to try - // to allocate a new region even if the max has been reached. - HeapWord* new_alloc_region_and_allocate(size_t word_size, bool force); + // Allocate a new region and use it to perform an allocation with minimum + // and desired size in words, and set that region as new active region. + // The force parameter will be passed on to G1CollectedHeap::allocate_new_alloc_region() + // and tells it to try to allocate a new region even if the max has been reached. + HeapWord* new_alloc_region_and_allocate(size_t min_word_size, + size_t desired_word_size, + bool force, + size_t* actual_word_size); + + // Notify the heap that the given region has just been retired. Allocated_bytes + // bytes have been allocated into it. + virtual void notify_region_retired(HeapRegion* alloc_region, size_t allocated_bytes) = 0; protected: + // For convenience as subclasses use it. + static G1CollectedHeap* _g1h; + + // Allocate a new region that is large enough to fit word_size into it. + virtual HeapRegion* allocate_new_region(size_t word_size, bool force) = 0; + // Allocate a new region and allocate at least min_word_size words into it, returning + // both the new heap region, the start address of the allocated block and the + // actual size of the allocation. + virtual HeapRegion* new_alloc_region_and_allocate_helper(size_t min_word_size, + size_t desired_word_size, + bool force, + HeapWord** result, + size_t* actual_word_size); + + void reset_alloc_region() { _alloc_region = _dummy_region; } + + G1AllocRegion(const char* name, bool bot_updates); + + // Release or retain the current allocation region after an unsuccessful allocation + // during GC. + virtual void release_or_retain_during_gc(); + +public: // Retire the active allocating region. If fill_up is true then make // sure that the region is full before we retire it so that no one // else can allocate out of it. // Returns the number of bytes that have been filled up during retire. - virtual size_t retire(bool fill_up); + virtual size_t retire_region(HeapRegion* region, bool fill_up); - // For convenience as subclasses use it. - static G1CollectedHeap* _g1h; - - virtual HeapRegion* allocate_new_region(size_t word_size, bool force) = 0; - virtual void retire_region(HeapRegion* alloc_region, - size_t allocated_bytes) = 0; - - G1AllocRegion(const char* name, bool bot_updates); - -public: static void setup(G1CollectedHeap* g1h, HeapRegion* dummy_region); HeapRegion* get() const { @@ -158,6 +161,24 @@ uint count() { return _count; } + bool bot_updates() const { return _bot_updates; } + + // Perform a MT-safe allocation out of the given region. See above about the + // _bot_updates comment. + static inline HeapWord* par_allocate(HeapRegion* alloc_region, + size_t word_size, + bool bot_updates); + // Perform a MT-safe allocation out of the given region, with the given + // minimum and desired size. Returns the actual size allocated (between + // minimum and desired size) in actual_word_size if the allocation has been + // successful. + // See above about the _bot_updates comment. + static inline HeapWord* par_allocate(HeapRegion* alloc_region, + size_t min_word_size, + size_t desired_word_size, + size_t* actual_word_size, + bool bot_updates); + // The following two are the building blocks for the allocation method. // First-level allocation: Should be called without holding a @@ -204,23 +225,34 @@ void init(); // This can be used to set the active region to a specific - // region. (Use Example: we try to retain the last old GC alloc + // region. (Usage example: we try to retain the last old GC alloc // region that we've used during a GC and we can use set() to // re-instate it at the beginning of the next GC.) void set(HeapRegion* alloc_region); // Should be called when we want to release the active region which // is returned after it's been retired. + // The remaining free space is not filled. virtual HeapRegion* release(); + void trace(const char* str, + size_t min_word_size = 0, + size_t desired_word_size = 0, + size_t actual_word_size = 0, + HeapWord* result = NULL) { + trace(str, _alloc_region, min_word_size, desired_word_size, actual_word_size, result); + } + #if G1_ALLOC_REGION_TRACING void trace(const char* str, + HeapRegion* r, size_t min_word_size = 0, size_t desired_word_size = 0, size_t actual_word_size = 0, HeapWord* result = NULL); #else // G1_ALLOC_REGION_TRACING void trace(const char* str, + HeapRegion* r, size_t min_word_size = 0, size_t desired_word_size = 0, size_t actual_word_size = 0, @@ -231,7 +263,7 @@ class MutatorAllocRegion : public G1AllocRegion { protected: virtual HeapRegion* allocate_new_region(size_t word_size, bool force); - virtual void retire_region(HeapRegion* alloc_region, size_t allocated_bytes); + virtual void notify_region_retired(HeapRegion* alloc_region, size_t allocated_bytes); public: MutatorAllocRegion() : G1AllocRegion("Mutator Alloc Region", false /* bot_updates */) { } @@ -242,15 +274,27 @@ protected: G1EvacStats* _stats; InCSetState::in_cset_state_t _purpose; + RetainedRegionsList _retained_regions; + + virtual HeapRegion* new_alloc_region_and_allocate_helper(size_t min_word_size, + size_t desired_word_size, + bool force, + HeapWord** result, + size_t* actual_word_size); virtual HeapRegion* allocate_new_region(size_t word_size, bool force); - virtual void retire_region(HeapRegion* alloc_region, size_t allocated_bytes); + virtual void notify_region_retired(HeapRegion* alloc_region, size_t allocated_bytes); - virtual size_t retire(bool fill_up); + virtual void release_or_retain_during_gc(); public: + virtual size_t retire_region(HeapRegion* region, bool fill_up); + + virtual HeapRegion* release(); + G1GCAllocRegion(const char* name, bool bot_updates, G1EvacStats* stats, InCSetState::in_cset_state_t purpose) - : G1AllocRegion(name, bot_updates), _stats(stats), _purpose(purpose) { + : G1AllocRegion(name, bot_updates), _stats(stats), _purpose(purpose), _retained_regions() { assert(stats != NULL, "Must pass non-NULL PLAB statistics"); + _retained_regions.set_region_allocator(this); } }; diff -r 509077a06114 src/share/vm/gc/g1/g1AllocRegion.inline.hpp --- a/src/share/vm/gc/g1/g1AllocRegion.inline.hpp Fri Nov 20 19:17:57 2015 +0000 +++ b/src/share/vm/gc/g1/g1AllocRegion.inline.hpp Tue Nov 24 10:17:21 2015 +0100 @@ -30,9 +30,9 @@ #define assert_alloc_region(p, message) \ do { \ - assert((p), "[%s] %s c: %u b: %s r: " PTR_FORMAT " u: " SIZE_FORMAT, \ + assert((p), "[%s] %s c: %u b: %s r: " PTR_FORMAT, \ _name, (message), _count, BOOL_TO_STR(_bot_updates), \ - p2i(_alloc_region), _used_bytes_before); \ + p2i(_alloc_region)); \ } while (0) @@ -108,10 +108,8 @@ return result; } - retire(true /* fill_up */); - result = new_alloc_region_and_allocate(desired_word_size, false /* force */); + result = new_alloc_region_and_allocate(min_word_size, desired_word_size, false /* force */, actual_word_size); if (result != NULL) { - *actual_word_size = desired_word_size; trace("alloc locked (second attempt)", min_word_size, desired_word_size, *actual_word_size, result); return result; } @@ -125,9 +123,11 @@ assert_alloc_region(_alloc_region != NULL, "not initialized properly"); trace("forcing alloc", word_size, word_size); - HeapWord* result = new_alloc_region_and_allocate(word_size, true /* force */); + size_t temp = 0; + HeapWord* result = new_alloc_region_and_allocate(word_size, word_size, true /* force */, &temp); if (result != NULL) { - trace("alloc forced", word_size, word_size, word_size, result); + assert(temp == word_size, "Expected " SIZE_FORMAT " word allocation, got " SIZE_FORMAT, word_size, temp); + trace("alloc forced", word_size, word_size, temp, result); return result; } trace("alloc forced failed", word_size, word_size); diff -r 509077a06114 src/share/vm/gc/g1/g1_globals.hpp --- a/src/share/vm/gc/g1/g1_globals.hpp Fri Nov 20 19:17:57 2015 +0000 +++ b/src/share/vm/gc/g1/g1_globals.hpp Tue Nov 24 10:17:21 2015 +0100 @@ -300,6 +300,10 @@ "Print some information about large object liveness " \ "at every young GC.") \ \ + experimental(bool, G1RetainAllocRegionsDuringGC, true, \ + "Try to retain and reuse allocation regions with some space left "\ + "during GC.") \ + \ experimental(uintx, G1OldCSetRegionThresholdPercent, 10, \ "An upper bound for the number of old CSet regions expressed " \ "as a percentage of the heap size.") \ diff -r 509077a06114 src/share/vm/gc/g1/heapRegion.cpp --- a/src/share/vm/gc/g1/heapRegion.cpp Fri Nov 20 19:17:57 2015 +0000 +++ b/src/share/vm/gc/g1/heapRegion.cpp Tue Nov 24 10:17:21 2015 +0100 @@ -254,7 +254,7 @@ #endif // ASSERT _young_index_in_cset(-1), _surv_rate_group(NULL), _age_index(-1), _rem_set(NULL), _recorded_rs_length(0), _predicted_elapsed_time_ms(0), - _predicted_bytes_to_copy(0) + _predicted_bytes_to_copy(0), _top_at_alloc_start(NULL), _next_in_retained(NULL) { _rem_set = new HeapRegionRemSet(sharedOffsetArray, this); assert(HeapRegionRemSet::num_par_rem_sets() > 0, "Invariant."); @@ -270,6 +270,9 @@ hr_clear(false /*par*/, false /*clear_space*/); set_top(bottom()); record_timestamp(); + + _top_at_alloc_start = NULL; + _next_in_retained = NULL; } CompactibleSpace* HeapRegion::next_compaction_space() const { diff -r 509077a06114 src/share/vm/gc/g1/heapRegion.hpp --- a/src/share/vm/gc/g1/heapRegion.hpp Fri Nov 20 19:17:57 2015 +0000 +++ b/src/share/vm/gc/g1/heapRegion.hpp Tue Nov 24 10:17:21 2015 +0100 @@ -328,7 +328,38 @@ // the total value for the collection set. size_t _predicted_bytes_to_copy; + // Top pointer at allocation start. + HeapWord* _top_at_alloc_start; + // Next heap region in retained list. + HeapRegion* _next_in_retained; + public: + void set_next_in_retained(HeapRegion* next) { + assert(_next_in_retained == NULL || next == NULL, "should not overwrite other value"); + _next_in_retained = next; + } + + HeapRegion* next_in_retained() const { return _next_in_retained; } + HeapRegion** next_in_retained_addr() { return &_next_in_retained; } + + void reset_after_alloc() { + _top_at_alloc_start = NULL; + _next_in_retained = NULL; + } + + void update_top_at_alloc_start(HeapWord* top) { + if (_top_at_alloc_start == NULL) { + _top_at_alloc_start = top; + } + } + + // Returns the number of bytes allocated between the last time allocation has been + // started and now. + size_t recently_allocated_bytes() const { + assert(_top_at_alloc_start != NULL, "top at evac start for region %u is NULL", _hrm_index); + return pointer_delta(top(), _top_at_alloc_start) * HeapWordSize; + } + HeapRegion(uint hrm_index, G1BlockOffsetSharedArray* sharedOffsetArray, MemRegion mr); diff -r 509077a06114 src/share/vm/gc/g1/heapRegion.inline.hpp --- a/src/share/vm/gc/g1/heapRegion.inline.hpp Fri Nov 20 19:17:57 2015 +0000 +++ b/src/share/vm/gc/g1/heapRegion.inline.hpp Tue Nov 24 10:17:21 2015 +0100 @@ -52,8 +52,8 @@ inline HeapWord* G1OffsetTableContigSpace::par_allocate_impl(size_t min_word_size, size_t desired_word_size, size_t* actual_size) { + HeapWord* obj = top(); do { - HeapWord* obj = top(); size_t available = pointer_delta(end(), obj); size_t want_to_allocate = MIN2(available, desired_word_size); if (want_to_allocate >= min_word_size) { @@ -67,6 +67,7 @@ *actual_size = want_to_allocate; return obj; } + obj = result; } else { return NULL; } diff -r 509077a06114 src/share/vm/gc/g1/retainedRegionsList.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/gc/g1/retainedRegionsList.cpp Tue Nov 24 10:17:21 2015 +0100 @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "gc/g1/g1AllocRegion.inline.hpp" +#include "gc/g1/heapRegion.inline.hpp" +#include "gc/g1/retainedRegionsList.hpp" + +#ifdef ASSERT +size_t RetainedRegionsList::check_list(HeapRegion* first, HeapRegion* to_add) { + size_t result = 0; + while (first != NULL) { + assert(to_add != first, "Element " PTR_FORMAT " already in list", p2i(to_add)); + result++; + first = first->next_in_retained(); + } + return result; +} +#endif + +void RetainedRegionsList::add_to_list(HeapRegion** list, HeapRegion* region) { +#ifdef ASSERT + size_t num_in_list = check_list(*list, region); +#endif + + region->set_next_in_retained(*list); + *list = region; + + _largest_in_retained = MAX2(_largest_in_retained, region->free() / HeapWordSize); + +#ifdef ASSERT + size_t num_in_list2 = check_list(*list, NULL); + assert((num_in_list + 1) == num_in_list2, "differing list sizes"); +#endif +} + +void RetainedRegionsList::retire_region(HeapRegion* region) { + _region_allocator->retire_region(region, true); +} + +void RetainedRegionsList::retire_regions(HeapRegion** list) { + HeapRegion* cur = *list; + while (cur != NULL) { + HeapRegion* next = cur->next_in_retained(); + retire_region(cur); + cur = next; + } + *list = NULL; +} + +bool RetainedRegionsList::may_retire_region(size_t const free_words) const { + return free_words * 100 <= HeapRegion::GrainWords * TargetPLABWastePct; +} + +void RetainedRegionsList::append(HeapRegion* r) { + size_t const remaining_words = r->free() / HeapWordSize; + if (may_retire_region(remaining_words)) { + retire_region(r); + } else { + add_to_list(&_first_retained, r); + } +} + +HeapRegion* RetainedRegionsList::get_region_and_allocate(size_t min_word_size, size_t desired_size, HeapWord** result, size_t* actual_size) { + *result = NULL; + + if (min_word_size > _largest_in_retained) { + return NULL; + } + + HeapRegion** prev = &_first_retained; + HeapRegion* r = _first_retained; + + size_t largest_in_retained = 0; + + while (r != NULL) { + size_t free_words = r->free() / HeapWordSize; + + if (free_words >= min_word_size) { + *result = G1AllocRegion::par_allocate(r, min_word_size, desired_size, actual_size, _region_allocator->bot_updates()); + if (*result != NULL) { + // Found and managed to allocate from the region. Unlink from list and return. + *prev = r->next_in_retained(); + r->set_next_in_retained(NULL); + + // We can not update the estimation about the largest free block in the list + // since we have not looked through all blocks. So keep the current largest. + largest_in_retained = _largest_in_retained; + break; + } + } + if (may_retire_region(free_words)) { + // The region has been filled up in the meantime. Unlink from list and retire. + HeapRegion* next = r->next_in_retained(); + r->set_next_in_retained(NULL); + retire_region(r); + + *prev = next; + r = next; + } else { + prev = r->next_in_retained_addr(); + r = r->next_in_retained(); + } + largest_in_retained = MAX2(largest_in_retained, free_words); + assert(*prev == r, "Prev and current pointer are inconsistent, previous " PTR_FORMAT " not pointing to current " PTR_FORMAT, p2i(*prev), p2i(r)); + } + + _largest_in_retained = largest_in_retained; + + return r; +} + +void RetainedRegionsList::retire_all() { + retire_regions(&_first_retained); +} diff -r 509077a06114 src/share/vm/gc/g1/retainedRegionsList.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/gc/g1/retainedRegionsList.hpp Tue Nov 24 10:17:21 2015 +0100 @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_VM_GC_G1_RETAINEDREGIONSLIST_HPP +#define SHARE_VM_GC_G1_RETAINEDREGIONSLIST_HPP + +#include "memory/allocation.hpp" + +class G1AllocRegion; +class HeapRegion; + +// A list of retained regions during the GC phase. These retained regions contain +// some space at the end of regions that are looked up for further allocation. +// +// During allocation, all regions get fed into this container: regions that are +// considered not worth further allocating into are automatically retired, while +// we keep holding onto regions that still contain a reasonable amount of bytes. +// +// Implements a first-fit algorithm to find the next region that contains sufficient +// space. +// +// Note that after appending a region to this list, threads might still have references +// to that region and continue allocating into it. We do not prevent that during +// insertion of the region into this list (by e.g. filling up the region with a dummy +// region) to avoid serialization. So any allocation needs to be atomic, and when +// we search for free space in the regions in this list, we need to be aware that +// in the meantime their free space might have decreased below the threshold to +// include it into this list. We automatically prune these regions from this list +// during that walk. +class RetainedRegionsList : public CHeapObj { + private: + // Used for retiring regions in this list. + G1AllocRegion* _region_allocator; + + HeapRegion* _first_retained; + + // An optimistic estimate of the largest available free block of memory in any + // of the regions. This might be wrong as regions added to this list may still + // be allocated into. Still it helps avoiding scanning through the list. + size_t _largest_in_retained; + + void add_to_list(HeapRegion** list, HeapRegion* region); + + void retire_region(HeapRegion* region); + void retire_regions(HeapRegion** list); + +#ifdef ASSERT + // Verifies that the to_add element is not in the list started by first yet. + // Returns the length of the list. + size_t check_list(HeapRegion* first, HeapRegion* to_add); +#endif + + // Should we retire a region with the given amount of free words? + bool may_retire_region(size_t const free_words) const; + + public: + RetainedRegionsList() : _region_allocator(NULL), _first_retained(NULL), _largest_in_retained(0) { } + + void set_region_allocator(G1AllocRegion* allocator) { _region_allocator = allocator; } + + // Add the given region to this list. + void append(HeapRegion* r); + // Get a region with at least min_word_size available space and allocate up to + // desired_size into it, returning the allocation result in result and actual_size. + // Returns NULL if no region in this list has been found. + HeapRegion* get_region_and_allocate(size_t min_word_size, size_t desired_size, HeapWord** result, size_t* actual_size); + + // Retire all regions in this list. + void retire_all(); +}; + +#endif /* SHARE_VM_GC_G1_RETAINEDREGIONSLIST_HPP */ diff -r 509077a06114 test/gc/g1/TestRegionEndFragmentation.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/gc/g1/TestRegionEndFragmentation.java Tue Nov 24 10:17:21 2015 +0100 @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test TestRegionEndFragmentation + * @bug 8067433 + * @summary Runs garbage collections with only medium objects surviving (> PLAB size). Medium object allocation causes heavy fragmentation at the end + * of regions, which stresses the G1 mechanism to retain regions during the whole GC. + * @key gc + * @requires vm.gc=="G1" | vm.gc=="null" + * @run main/othervm -XX:+UseG1GC -XX:-ResizePLAB -Xmx512M -XX:YoungPLABSize=1024 -XX:OldPLABSize=1024 -XX:G1HeapRegionSize=1M TestRegionEndFragmentation + */ + +import java.util.Random; + +public class TestRegionEndFragmentation { + public static final int MAX_MILLIS_FOR_RUN = 50 * 1000; + + public static final int OBJECT_SIZE_SPAN = 200 * 1024; + public static final int MIN_OBJECT_SIZE = 1024; + + /** + * Churn through a large amount of medium sized objects that cause a significant amount of fragmentation at the + * end of regions when garbage collecting. + * Run settings make sure that during GC we fragment a lot. + */ + public static void main(String[] args) throws Exception { + Random r = new Random(1024); // Initialize the RNG to a particular value to get repeatable object sizes. + + Object[] array = new Object[2048]; + + long start_millis = System.currentTimeMillis(); + + for (int i = 0; i < 500; i++) { + long current_millis = System.currentTimeMillis(); + if ((current_millis - start_millis) > MAX_MILLIS_FOR_RUN) { + System.out.println("Finishing test because maximum runtime exceeded"); + break; + } + for (int j = 0; j < 1000; j++) { + array[i % array.length] = new byte[r.nextInt(OBJECT_SIZE_SPAN) + MIN_OBJECT_SIZE]; + } + } + } +} +