8241141: Restructure humongous object allocation in G1

Reviewed-by: tschatzl, kbarrett
This commit is contained in:
Stefan Johansson 2020-04-08 18:38:31 +02:00
parent dd4e04d6f5
commit 52ea4802f1
8 changed files with 162 additions and 103 deletions

View File

@ -209,14 +209,15 @@ HeapRegion* G1CollectedHeap::new_region(size_t word_size,
} }
HeapWord* HeapWord*
G1CollectedHeap::humongous_obj_allocate_initialize_regions(uint first, G1CollectedHeap::humongous_obj_allocate_initialize_regions(HeapRegion* first_hr,
uint num_regions, uint num_regions,
size_t word_size) { size_t word_size) {
assert(first != G1_NO_HRM_INDEX, "pre-condition"); assert(first_hr != NULL, "pre-condition");
assert(is_humongous(word_size), "word_size should be humongous"); assert(is_humongous(word_size), "word_size should be humongous");
assert(num_regions * HeapRegion::GrainWords >= word_size, "pre-condition"); assert(num_regions * HeapRegion::GrainWords >= word_size, "pre-condition");
// Index of last region in the series. // Index of last region in the series.
uint first = first_hr->hrm_index();
uint last = first + num_regions - 1; uint last = first + num_regions - 1;
// We need to initialize the region(s) we just discovered. This is // We need to initialize the region(s) we just discovered. This is
@ -231,10 +232,8 @@ G1CollectedHeap::humongous_obj_allocate_initialize_regions(uint first,
size_t word_size_sum = (size_t) num_regions * HeapRegion::GrainWords; size_t word_size_sum = (size_t) num_regions * HeapRegion::GrainWords;
assert(word_size <= word_size_sum, "sanity"); assert(word_size <= word_size_sum, "sanity");
// This will be the "starts humongous" region. // The passed in hr will be the "starts humongous" region. The header
HeapRegion* first_hr = region_at(first); // of the new object will be placed at the bottom of this region.
// The header of the new object will be placed at the bottom of
// the first region.
HeapWord* new_obj = first_hr->bottom(); HeapWord* new_obj = first_hr->bottom();
// This will be the new top of the new object. // This will be the new top of the new object.
HeapWord* obj_top = new_obj + word_size; HeapWord* obj_top = new_obj + word_size;
@ -340,57 +339,28 @@ HeapWord* G1CollectedHeap::humongous_obj_allocate(size_t word_size) {
_verifier->verify_region_sets_optional(); _verifier->verify_region_sets_optional();
uint first = G1_NO_HRM_INDEX;
uint obj_regions = (uint) humongous_obj_size_in_regions(word_size); uint obj_regions = (uint) humongous_obj_size_in_regions(word_size);
if (obj_regions == 1) { // Policy: First try to allocate a humongous object in the free list.
// Only one region to allocate, try to use a fast path by directly allocating HeapRegion* humongous_start = _hrm->allocate_humongous(obj_regions);
// from the free lists. Do not try to expand here, we will potentially do that if (humongous_start == NULL) {
// later.
HeapRegion* hr = new_region(word_size, HeapRegionType::Humongous, false /* do_expand */);
if (hr != NULL) {
first = hr->hrm_index();
}
} else {
// Policy: Try only empty regions (i.e. already committed first). Maybe we
// are lucky enough to find some.
first = _hrm->find_contiguous_only_empty(obj_regions);
if (first != G1_NO_HRM_INDEX) {
_hrm->allocate_free_regions_starting_at(first, obj_regions);
}
}
if (first == G1_NO_HRM_INDEX) {
// Policy: We could not find enough regions for the humongous object in the // Policy: We could not find enough regions for the humongous object in the
// free list. Look through the heap to find a mix of free and uncommitted regions. // free list. Look through the heap to find a mix of free and uncommitted regions.
// If so, try expansion. // If so, expand the heap and allocate the humongous object.
first = _hrm->find_contiguous_empty_or_unavailable(obj_regions); humongous_start = _hrm->expand_and_allocate_humongous(obj_regions);
if (first != G1_NO_HRM_INDEX) { if (humongous_start != NULL) {
// We found something. Make sure these regions are committed, i.e. expand // We managed to find a region by expanding the heap.
// the heap. Alternatively we could do a defragmentation GC. log_debug(gc, ergo, heap)("Heap expansion (humongous allocation request). Allocation request: " SIZE_FORMAT "B",
log_debug(gc, ergo, heap)("Attempt heap expansion (humongous allocation request failed). Allocation request: " SIZE_FORMAT "B", word_size * HeapWordSize);
word_size * HeapWordSize);
_hrm->expand_at(first, obj_regions, workers());
policy()->record_new_heap_size(num_regions()); policy()->record_new_heap_size(num_regions());
#ifdef ASSERT
for (uint i = first; i < first + obj_regions; ++i) {
HeapRegion* hr = region_at(i);
assert(hr->is_free(), "sanity");
assert(hr->is_empty(), "sanity");
assert(is_on_master_free_list(hr), "sanity");
}
#endif
_hrm->allocate_free_regions_starting_at(first, obj_regions);
} else { } else {
// Policy: Potentially trigger a defragmentation GC. // Policy: Potentially trigger a defragmentation GC.
} }
} }
HeapWord* result = NULL; HeapWord* result = NULL;
if (first != G1_NO_HRM_INDEX) { if (humongous_start != NULL) {
result = humongous_obj_allocate_initialize_regions(first, obj_regions, word_size); result = humongous_obj_allocate_initialize_regions(humongous_start, obj_regions, word_size);
assert(result != NULL, "it should always return a valid result"); assert(result != NULL, "it should always return a valid result");
// A successful humongous object allocation changes the used space // A successful humongous object allocation changes the used space
@ -4892,8 +4862,7 @@ HeapRegion* G1CollectedHeap::alloc_highest_free_region() {
log_debug(gc, ergo, heap)("Attempt heap expansion (requested address range outside heap bounds). region size: " SIZE_FORMAT "B", log_debug(gc, ergo, heap)("Attempt heap expansion (requested address range outside heap bounds). region size: " SIZE_FORMAT "B",
HeapRegion::GrainWords * HeapWordSize); HeapRegion::GrainWords * HeapWordSize);
} }
_hrm->allocate_free_regions_starting_at(index, 1); return _hrm->allocate_free_regions_starting_at(index, 1);
return region_at(index);
} }
return NULL; return NULL;
} }

View File

@ -409,7 +409,7 @@ private:
// Initialize a contiguous set of free regions of length num_regions // Initialize a contiguous set of free regions of length num_regions
// and starting at index first so that they appear as a single // and starting at index first so that they appear as a single
// humongous region. // humongous region.
HeapWord* humongous_obj_allocate_initialize_regions(uint first, HeapWord* humongous_obj_allocate_initialize_regions(HeapRegion* first_hr,
uint num_regions, uint num_regions,
size_t word_size); size_t word_size);

View File

@ -135,6 +135,35 @@ HeapRegion* HeapRegionManager::allocate_free_region(HeapRegionType type, uint re
return hr; return hr;
} }
HeapRegion* HeapRegionManager::allocate_humongous_from_free_list(uint num_regions) {
uint candidate = find_contiguous_in_free_list(num_regions);
if (candidate == G1_NO_HRM_INDEX) {
return NULL;
}
return allocate_free_regions_starting_at(candidate, num_regions);
}
HeapRegion* HeapRegionManager::allocate_humongous_allow_expand(uint num_regions) {
uint candidate = find_contiguous_allow_expand(num_regions);
if (candidate == G1_NO_HRM_INDEX) {
return NULL;
}
expand_exact(candidate, num_regions, G1CollectedHeap::heap()->workers());
return allocate_free_regions_starting_at(candidate, num_regions);
}
HeapRegion* HeapRegionManager::allocate_humongous(uint num_regions) {
// Special case a single region to avoid expensive search.
if (num_regions == 1) {
return allocate_free_region(HeapRegionType::Humongous, G1NUMA::AnyNodeIndex);
}
return allocate_humongous_from_free_list(num_regions);
}
HeapRegion* HeapRegionManager::expand_and_allocate_humongous(uint num_regions) {
return allocate_humongous_allow_expand(num_regions);
}
#ifdef ASSERT #ifdef ASSERT
bool HeapRegionManager::is_free(HeapRegion* hr) const { bool HeapRegionManager::is_free(HeapRegion* hr) const {
return _free_list.contains(hr); return _free_list.contains(hr);
@ -271,6 +300,19 @@ uint HeapRegionManager::expand_at(uint start, uint num_regions, WorkGang* pretou
return expanded; return expanded;
} }
void HeapRegionManager::expand_exact(uint start, uint num_regions, WorkGang* pretouch_workers) {
assert(num_regions != 0, "Need to request at least one region");
uint end = start + num_regions;
for (uint i = start; i < end; i++) {
if (!is_available(i)) {
make_regions_available(i, 1, pretouch_workers);
}
}
verify_optional();
}
uint HeapRegionManager::expand_on_preferred_node(uint preferred_index) { uint HeapRegionManager::expand_on_preferred_node(uint preferred_index) {
uint expand_candidate = UINT_MAX; uint expand_candidate = UINT_MAX;
for (uint i = 0; i < max_length(); i++) { for (uint i = 0; i < max_length(); i++) {
@ -291,7 +333,7 @@ uint HeapRegionManager::expand_on_preferred_node(uint preferred_index) {
return 0; return 0;
} }
make_regions_available(expand_candidate, 1, NULL); expand_exact(expand_candidate, 1, NULL);
return 1; return 1;
} }
@ -300,36 +342,61 @@ bool HeapRegionManager::is_on_preferred_index(uint region_index, uint preferred_
return region_node_index == preferred_node_index; return region_node_index == preferred_node_index;
} }
uint HeapRegionManager::find_contiguous(size_t num, bool empty_only) { void HeapRegionManager::guarantee_contiguous_range(uint start, uint num_regions) {
uint found = 0; // General sanity check, regions found should either be available and empty
size_t length_found = 0; // or not available so that we can make them available and use them.
uint cur = 0; for (uint i = start; i < (start + num_regions); i++) {
HeapRegion* hr = _regions.get_by_index(i);
while (length_found < num && cur < max_length()) { guarantee(!is_available(i) || hr->is_free(),
HeapRegion* hr = _regions.get_by_index(cur); "Found region sequence starting at " UINT32_FORMAT ", length " UINT32_FORMAT
if ((!empty_only && !is_available(cur)) || (is_available(cur) && hr != NULL && hr->is_empty())) { " that is not free at " UINT32_FORMAT ". Hr is " PTR_FORMAT ", type is %s",
// This region is a potential candidate for allocation into. start, num_regions, i, p2i(hr), hr->get_type_str());
length_found++;
} else {
// This region is not a candidate. The next region is the next possible one.
found = cur + 1;
length_found = 0;
}
cur++;
} }
}
if (length_found == num) { uint HeapRegionManager::find_contiguous_in_range(uint start, uint end, uint num_regions) {
for (uint i = found; i < (found + num); i++) { assert(start <= end, "precondition");
HeapRegion* hr = _regions.get_by_index(i); assert(num_regions >= 1, "precondition");
// sanity check uint candidate = start; // First region in candidate sequence.
guarantee((!empty_only && !is_available(i)) || (is_available(i) && hr != NULL && hr->is_empty()), uint unchecked = candidate; // First unchecked region in candidate.
"Found region sequence starting at " UINT32_FORMAT ", length " SIZE_FORMAT // While the candidate sequence fits in the range...
" that is not empty at " UINT32_FORMAT ". Hr is " PTR_FORMAT, found, num, i, p2i(hr)); while (num_regions <= (end - candidate)) {
// Walk backward over the regions for the current candidate.
for (uint i = candidate + num_regions - 1; true; --i) {
if (is_available(i) && !at(i)->is_free()) {
// Region i can't be used, so restart with i+1 as the start
// of a new candidate sequence, and with the region after the
// old candidate sequence being the first unchecked region.
unchecked = candidate + num_regions;
candidate = i + 1;
break;
} else if (i == unchecked) {
// All regions of candidate sequence have passed check.
guarantee_contiguous_range(candidate, num_regions);
return candidate;
}
} }
return found;
} else {
return G1_NO_HRM_INDEX;
} }
return G1_NO_HRM_INDEX;
}
uint HeapRegionManager::find_contiguous_in_free_list(uint num_regions) {
BitMap::idx_t range_start = 0;
BitMap::idx_t range_end = range_start;
uint candidate = G1_NO_HRM_INDEX;
do {
range_start = _available_map.get_next_one_offset(range_end);
range_end = _available_map.get_next_zero_offset(range_start);
candidate = find_contiguous_in_range((uint) range_start, (uint) range_end, num_regions);
} while (candidate == G1_NO_HRM_INDEX && range_end < max_length());
return candidate;
}
uint HeapRegionManager::find_contiguous_allow_expand(uint num_regions) {
// Find any candidate.
return find_contiguous_in_range(0, max_length(), num_regions);
} }
HeapRegion* HeapRegionManager::next_region_in_heap(const HeapRegion* r) const { HeapRegion* HeapRegionManager::next_region_in_heap(const HeapRegion* r) const {

View File

@ -94,11 +94,19 @@ class HeapRegionManager: public CHeapObj<mtGC> {
// Notify other data structures about change in the heap layout. // Notify other data structures about change in the heap layout.
void update_committed_space(HeapWord* old_end, HeapWord* new_end); void update_committed_space(HeapWord* old_end, HeapWord* new_end);
// Find a contiguous set of empty or uncommitted regions of length num and return // Find a contiguous set of empty or uncommitted regions of length num_regions and return
// the index of the first region or G1_NO_HRM_INDEX if the search was unsuccessful. // the index of the first region or G1_NO_HRM_INDEX if the search was unsuccessful.
// If only_empty is true, only empty regions are considered. // Start and end defines the range to seek in, policy is first-fit.
// Searches from bottom to top of the heap, doing a first-fit. uint find_contiguous_in_range(uint start, uint end, uint num_regions);
uint find_contiguous(size_t num, bool only_empty); // Find a contiguous set of empty regions of length num_regions. Returns the start index
// of that set, or G1_NO_HRM_INDEX.
uint find_contiguous_in_free_list(uint num_regions);
// Find a contiguous set of empty or unavailable regions of length num_regions. Returns the
// start index of that set, or G1_NO_HRM_INDEX.
uint find_contiguous_allow_expand(uint num_regions);
void guarantee_contiguous_range(uint start, uint num_regions) ;
// Finds the next sequence of unavailable regions starting from start_idx. Returns the // Finds the next sequence of unavailable regions starting from start_idx. Returns the
// length of the sequence found. If this result is zero, no such sequence could be found, // length of the sequence found. If this result is zero, no such sequence could be found,
// otherwise res_idx indicates the start index of these regions. // otherwise res_idx indicates the start index of these regions.
@ -122,6 +130,14 @@ protected:
void uncommit_regions(uint index, size_t num_regions = 1); void uncommit_regions(uint index, size_t num_regions = 1);
// Allocate a new HeapRegion for the given index. // Allocate a new HeapRegion for the given index.
HeapRegion* new_heap_region(uint hrm_index); HeapRegion* new_heap_region(uint hrm_index);
// Humongous allocation helpers
virtual HeapRegion* allocate_humongous_from_free_list(uint num_regions);
virtual HeapRegion* allocate_humongous_allow_expand(uint num_regions);
// Expand helper for cases when the regions to expand are well defined.
void expand_exact(uint start, uint num_regions, WorkGang* pretouch_workers);
#ifdef ASSERT #ifdef ASSERT
public: public:
bool is_free(HeapRegion* hr) const; bool is_free(HeapRegion* hr) const;
@ -183,7 +199,13 @@ public:
// Allocate a free region with specific node index. If fails allocate with next node index. // Allocate a free region with specific node index. If fails allocate with next node index.
virtual HeapRegion* allocate_free_region(HeapRegionType type, uint requested_node_index); virtual HeapRegion* allocate_free_region(HeapRegionType type, uint requested_node_index);
inline void allocate_free_regions_starting_at(uint first, uint num_regions); // Allocate a humongous object from the free list
HeapRegion* allocate_humongous(uint num_regions);
// Allocate a humongous object by expanding the heap
HeapRegion* expand_and_allocate_humongous(uint num_regions);
inline HeapRegion* allocate_free_regions_starting_at(uint first, uint num_regions);
// Remove all regions from the free list. // Remove all regions from the free list.
void remove_all_free_regions() { void remove_all_free_regions() {
@ -233,13 +255,6 @@ public:
// Try to expand on the given node index. // Try to expand on the given node index.
virtual uint expand_on_preferred_node(uint node_index); virtual uint expand_on_preferred_node(uint node_index);
// Find a contiguous set of empty regions of length num. Returns the start index of
// that set, or G1_NO_HRM_INDEX.
virtual uint find_contiguous_only_empty(size_t num) { return find_contiguous(num, true); }
// Find a contiguous set of empty or unavailable regions of length num. Returns the
// start index of that set, or G1_NO_HRM_INDEX.
virtual uint find_contiguous_empty_or_unavailable(size_t num) { return find_contiguous(num, false); }
HeapRegion* next_region_in_heap(const HeapRegion* r) const; HeapRegion* next_region_in_heap(const HeapRegion* r) const;
// Find the highest free or uncommitted region in the reserved heap, // Find the highest free or uncommitted region in the reserved heap,

View File

@ -73,8 +73,10 @@ inline void HeapRegionManager::insert_into_free_list(HeapRegion* hr) {
_free_list.add_ordered(hr); _free_list.add_ordered(hr);
} }
inline void HeapRegionManager::allocate_free_regions_starting_at(uint first, uint num_regions) { inline HeapRegion* HeapRegionManager::allocate_free_regions_starting_at(uint first, uint num_regions) {
_free_list.remove_starting_at(at(first), num_regions); HeapRegion* start = at(first);
_free_list.remove_starting_at(start, num_regions);
return start;
} }
#endif // SHARE_GC_G1_HEAPREGIONMANAGER_INLINE_HPP #endif // SHARE_GC_G1_HEAPREGIONMANAGER_INLINE_HPP

View File

@ -229,7 +229,7 @@ public:
// Remove all (contiguous) regions from first to first + num_regions -1 from // Remove all (contiguous) regions from first to first + num_regions -1 from
// this list. // this list.
// Num_regions must be > 1. // Num_regions must be >= 1.
void remove_starting_at(HeapRegion* first, uint num_regions); void remove_starting_at(HeapRegion* first, uint num_regions);
virtual void verify(); virtual void verify();

View File

@ -325,18 +325,28 @@ HeapRegion* HeterogeneousHeapRegionManager::allocate_free_region(HeapRegionType
return hr; return hr;
} }
uint HeterogeneousHeapRegionManager::find_contiguous_only_empty(size_t num) { HeapRegion* HeterogeneousHeapRegionManager::allocate_humongous_from_free_list(uint num_regions) {
if (has_borrowed_regions()) { if (has_borrowed_regions()) {
return G1_NO_HRM_INDEX; return NULL;
} }
return find_contiguous(start_index_of_nvdimm(), end_index_of_nvdimm(), num, true); uint candidate = find_contiguous(start_index_of_nvdimm(), end_index_of_nvdimm(), num_regions, true);
if (candidate == G1_NO_HRM_INDEX) {
return NULL;
}
return allocate_free_regions_starting_at(candidate, num_regions);
} }
uint HeterogeneousHeapRegionManager::find_contiguous_empty_or_unavailable(size_t num) { HeapRegion* HeterogeneousHeapRegionManager::allocate_humongous_allow_expand(uint num_regions) {
if (has_borrowed_regions()) { if (has_borrowed_regions()) {
return G1_NO_HRM_INDEX; return NULL;
} }
return find_contiguous(start_index_of_nvdimm(), end_index_of_nvdimm(), num, false); uint candidate = find_contiguous(start_index_of_nvdimm(), end_index_of_nvdimm(), num_regions, false);
if (candidate == G1_NO_HRM_INDEX) {
return NULL;
}
expand_exact(candidate, num_regions, NULL);
return allocate_free_regions_starting_at(candidate, num_regions);
} }
uint HeterogeneousHeapRegionManager::find_contiguous(size_t start, size_t end, size_t num, bool empty_only) { uint HeterogeneousHeapRegionManager::find_contiguous(size_t start, size_t end, size_t num, bool empty_only) {

View File

@ -120,6 +120,8 @@ public:
void prepare_for_full_collection_end(); void prepare_for_full_collection_end();
virtual HeapRegion* allocate_free_region(HeapRegionType type, uint node_index); virtual HeapRegion* allocate_free_region(HeapRegionType type, uint node_index);
virtual HeapRegion* allocate_humongous_from_free_list(uint num_regions);
virtual HeapRegion* allocate_humongous_allow_expand(uint num_regions);
// Return maximum number of regions that heap can expand to. // Return maximum number of regions that heap can expand to.
uint max_expandable_length() const; uint max_expandable_length() const;
@ -130,12 +132,6 @@ public:
// Override. // Override.
uint expand_at(uint start, uint num_regions, WorkGang* pretouch_workers); uint expand_at(uint start, uint num_regions, WorkGang* pretouch_workers);
// Override. This function is called for humongous allocation, so we need to find empty regions in nv-dimm.
uint find_contiguous_only_empty(size_t num);
// Override. This function is called for humongous allocation, so we need to find empty or unavailable regions in nv-dimm.
uint find_contiguous_empty_or_unavailable(size_t num);
// Overrides base class implementation to find highest free region in dram. // Overrides base class implementation to find highest free region in dram.
uint find_highest_free(bool* expanded); uint find_highest_free(bool* expanded);