1 /* 2 * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 #include "precompiled.hpp" 25 #include "gc/z/zArray.inline.hpp" 26 #include "gc/z/zPage.inline.hpp" 27 #include "gc/z/zRelocationSet.hpp" 28 #include "gc/z/zRelocationSetSelector.inline.hpp" 29 #include "logging/log.hpp" 30 #include "runtime/globals.hpp" 31 #include "utilities/debug.hpp" 32 33 ZRelocationSetSelectorGroupStats::ZRelocationSetSelectorGroupStats() : 34 _npages(0), 35 _total(0), 36 _live(0), 37 _garbage(0), 38 _empty(0), 39 _compacting_from(0), 40 _compacting_to(0) {} 41 42 ZRelocationSetSelectorGroup::ZRelocationSetSelectorGroup(const char* name, 43 size_t page_size, 44 size_t object_size_limit) : 45 _name(name), 46 _page_size(page_size), 47 _object_size_limit(object_size_limit), 48 _fragmentation_limit(page_size * (ZFragmentationLimit / 100)), 49 _registered_pages(), 50 _sorted_pages(NULL), 51 _nselected(0), 52 _stats() {} 53 54 ZRelocationSetSelectorGroup::~ZRelocationSetSelectorGroup() { 55 FREE_C_HEAP_ARRAY(ZPage*, _sorted_pages); 56 } 57 58 void ZRelocationSetSelectorGroup::register_live_page(ZPage* page) { 59 const uint8_t type = page->type(); 60 const size_t size = page->size(); 61 const size_t live = page->live_bytes(); 62 const size_t garbage = size - live; 63 64 if (garbage > _fragmentation_limit) { 65 _registered_pages.add(page); 66 } 67 68 _stats._npages++; 69 _stats._total += size; 70 _stats._live += live; 71 _stats._garbage += garbage; 72 } 73 74 void ZRelocationSetSelectorGroup::register_garbage_page(ZPage* page) { 75 const size_t size = page->size(); 76 77 _stats._npages++; 78 _stats._total += size; 79 _stats._garbage += size; 80 _stats._empty += size; 81 } 82 83 void ZRelocationSetSelectorGroup::semi_sort() { 84 // Semi-sort registered pages by live bytes in ascending order 85 const size_t npartitions_shift = 11; 86 const size_t npartitions = (size_t)1 << npartitions_shift; 87 const size_t partition_size = _page_size >> npartitions_shift; 88 const size_t partition_size_shift = exact_log2(partition_size); 89 const size_t npages = _registered_pages.size(); 90 91 // Partition slots/fingers 92 size_t partitions[npartitions]; 93 94 // Allocate destination array 95 assert(_sorted_pages == NULL, "Already initialized"); 96 _sorted_pages = NEW_C_HEAP_ARRAY(ZPage*, npages, mtGC); 97 debug_only(memset(_sorted_pages, 0, npages * sizeof(ZPage*))); 98 99 // Calculate partition slots 100 memset(partitions, 0, sizeof(partitions)); 101 ZArrayIterator<ZPage*> iter1(&_registered_pages); 102 for (ZPage* page; iter1.next(&page);) { 103 const size_t index = page->live_bytes() >> partition_size_shift; 104 partitions[index]++; 105 } 106 107 // Calculate partition fingers 108 size_t finger = 0; 109 for (size_t i = 0; i < npartitions; i++) { 110 const size_t slots = partitions[i]; 111 partitions[i] = finger; 112 finger += slots; 113 } 114 115 // Sort pages into partitions 116 ZArrayIterator<ZPage*> iter2(&_registered_pages); 117 for (ZPage* page; iter2.next(&page);) { 118 const size_t index = page->live_bytes() >> partition_size_shift; 119 const size_t finger = partitions[index]++; 120 assert(_sorted_pages[finger] == NULL, "Invalid finger"); 121 _sorted_pages[finger] = page; 122 } 123 } 124 125 void ZRelocationSetSelectorGroup::select() { 126 if (_page_size == 0) { 127 // Page type disabled 128 return; 129 } 130 131 // Calculate the number of pages to relocate by successively including pages in 132 // a candidate relocation set and calculate the maximum space requirement for 133 // their live objects. 134 const size_t npages = _registered_pages.size(); 135 size_t selected_from = 0; 136 size_t selected_to = 0; 137 size_t from_size = 0; 138 139 semi_sort(); 140 141 for (size_t from = 1; from <= npages; from++) { 142 // Add page to the candidate relocation set 143 from_size += _sorted_pages[from - 1]->live_bytes(); 144 145 // Calculate the maximum number of pages needed by the candidate relocation set. 146 // By subtracting the object size limit from the pages size we get the maximum 147 // number of pages that the relocation set is guaranteed to fit in, regardless 148 // of in which order the objects are relocated. 149 const size_t to = ceil((double)(from_size) / (double)(_page_size - _object_size_limit)); 150 151 // Calculate the relative difference in reclaimable space compared to our 152 // currently selected final relocation set. If this number is larger than the 153 // acceptable fragmentation limit, then the current candidate relocation set 154 // becomes our new final relocation set. 155 const size_t diff_from = from - selected_from; 156 const size_t diff_to = to - selected_to; 157 const double diff_reclaimable = 100 - percent_of(diff_to, diff_from); 158 if (diff_reclaimable > ZFragmentationLimit) { 159 selected_from = from; 160 selected_to = to; 161 } 162 163 log_trace(gc, reloc)("Candidate Relocation Set (%s Pages): " 164 SIZE_FORMAT "->" SIZE_FORMAT ", %.1f%% relative defragmentation, %s", 165 _name, from, to, diff_reclaimable, (selected_from == from) ? "Selected" : "Rejected"); 166 } 167 168 // Finalize selection 169 _nselected = selected_from; 170 171 // Update statistics 172 _stats._compacting_from = selected_from * _page_size; 173 _stats._compacting_to = selected_to * _page_size; 174 175 log_trace(gc, reloc)("Relocation Set (%s Pages): " SIZE_FORMAT "->" SIZE_FORMAT ", " SIZE_FORMAT " skipped", 176 _name, selected_from, selected_to, npages - _nselected); 177 } 178 179 ZRelocationSetSelector::ZRelocationSetSelector() : 180 _small("Small", ZPageSizeSmall, ZObjectSizeLimitSmall), 181 _medium("Medium", ZPageSizeMedium, ZObjectSizeLimitMedium), 182 _large("Large", 0 /* page_size */, 0 /* object_size_limit */) {} 183 184 void ZRelocationSetSelector::register_live_page(ZPage* page) { 185 const uint8_t type = page->type(); 186 187 if (type == ZPageTypeSmall) { 188 _small.register_live_page(page); 189 } else if (type == ZPageTypeMedium) { 190 _medium.register_live_page(page); 191 } else { 192 _large.register_live_page(page); 193 } 194 } 195 196 void ZRelocationSetSelector::register_garbage_page(ZPage* page) { 197 const uint8_t type = page->type(); 198 199 if (type == ZPageTypeSmall) { 200 _small.register_garbage_page(page); 201 } else if (type == ZPageTypeMedium) { 202 _medium.register_garbage_page(page); 203 } else { 204 _large.register_garbage_page(page); 205 } 206 } 207 208 void ZRelocationSetSelector::select(ZRelocationSet* relocation_set) { 209 // Select pages to relocate. The resulting relocation set will be 210 // sorted such that medium pages comes first, followed by small 211 // pages. Pages within each page group will be semi-sorted by live 212 // bytes in ascending order. Relocating pages in this order allows 213 // us to start reclaiming memory more quickly. 214 215 // Select pages from each group, except large 216 _medium.select(); 217 _small.select(); 218 219 // Populate relocation set 220 relocation_set->populate(_medium.selected(), _medium.nselected(), 221 _small.selected(), _small.nselected()); 222 } 223 224 ZRelocationSetSelectorStats ZRelocationSetSelector::stats() const { 225 ZRelocationSetSelectorStats stats; 226 stats._small = _small.stats(); 227 stats._medium = _medium.stats(); 228 stats._large = _large.stats(); 229 return stats; 230 }