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 "jfr/jfrEvents.hpp"
  30 #include "logging/log.hpp"
  31 #include "runtime/globals.hpp"
  32 #include "utilities/debug.hpp"
  33 #include "utilities/powerOfTwo.hpp"
  34 
  35 ZRelocationSetSelectorGroupStats::ZRelocationSetSelectorGroupStats() :
  36     _npages(0),
  37     _total(0),
  38     _live(0),
  39     _garbage(0),
  40     _empty(0),
  41     _compacting_from(0),
  42     _compacting_to(0) {}
  43 
  44 ZRelocationSetSelectorGroup::ZRelocationSetSelectorGroup(const char* name,
  45                                                          uint8_t page_type,
  46                                                          size_t page_size,
  47                                                          size_t object_size_limit) :
  48     _name(name),
  49     _page_type(page_type),
  50     _page_size(page_size),
  51     _object_size_limit(object_size_limit),
  52     _fragmentation_limit(page_size * (ZFragmentationLimit / 100)),
  53     _registered_pages(),
  54     _sorted_pages(NULL),
  55     _nselected(0),
  56     _stats() {}
  57 
  58 ZRelocationSetSelectorGroup::~ZRelocationSetSelectorGroup() {
  59   FREE_C_HEAP_ARRAY(ZPage*, _sorted_pages);
  60 }
  61 
  62 void ZRelocationSetSelectorGroup::register_live_page(ZPage* page) {
  63   const uint8_t type = page->type();
  64   const size_t size = page->size();
  65   const size_t live = page->live_bytes();
  66   const size_t garbage = size - live;
  67 
  68   if (garbage > _fragmentation_limit) {
  69     _registered_pages.append(page);
  70   }
  71 
  72   _stats._npages++;
  73   _stats._total += size;
  74   _stats._live += live;
  75   _stats._garbage += garbage;
  76 }
  77 
  78 void ZRelocationSetSelectorGroup::register_garbage_page(ZPage* page) {
  79   const size_t size = page->size();
  80 
  81   _stats._npages++;
  82   _stats._total += size;
  83   _stats._garbage += size;
  84   _stats._empty += size;
  85 }
  86 
  87 bool ZRelocationSetSelectorGroup::is_disabled() {
  88   // Medium pages are disabled when their page size is zero
  89   return _page_type == ZPageTypeMedium && _page_size == 0;
  90 }
  91 
  92 bool ZRelocationSetSelectorGroup::is_selectable() {
  93   // Large pages are not selectable
  94   return _page_type != ZPageTypeLarge;
  95 }
  96 
  97 void ZRelocationSetSelectorGroup::semi_sort() {
  98   // Semi-sort registered pages by live bytes in ascending order
  99   const size_t npartitions_shift = 11;
 100   const size_t npartitions = (size_t)1 << npartitions_shift;
 101   const size_t partition_size = _page_size >> npartitions_shift;
 102   const size_t partition_size_shift = exact_log2(partition_size);
 103   const size_t npages = _registered_pages.length();
 104 
 105   // Partition slots/fingers
 106   size_t partitions[npartitions];
 107 
 108   // Allocate destination array
 109   assert(_sorted_pages == NULL, "Already initialized");
 110   _sorted_pages = NEW_C_HEAP_ARRAY(ZPage*, npages, mtGC);
 111   debug_only(memset(_sorted_pages, 0, npages * sizeof(ZPage*)));
 112 
 113   // Calculate partition slots
 114   memset(partitions, 0, sizeof(partitions));
 115   ZArrayIterator<ZPage*> iter1(&_registered_pages);
 116   for (ZPage* page; iter1.next(&page);) {
 117     const size_t index = page->live_bytes() >> partition_size_shift;
 118     partitions[index]++;
 119   }
 120 
 121   // Calculate partition fingers
 122   size_t finger = 0;
 123   for (size_t i = 0; i < npartitions; i++) {
 124     const size_t slots = partitions[i];
 125     partitions[i] = finger;
 126     finger += slots;
 127   }
 128 
 129   // Sort pages into partitions
 130   ZArrayIterator<ZPage*> iter2(&_registered_pages);
 131   for (ZPage* page; iter2.next(&page);) {
 132     const size_t index = page->live_bytes() >> partition_size_shift;
 133     const size_t finger = partitions[index]++;
 134     assert(_sorted_pages[finger] == NULL, "Invalid finger");
 135     _sorted_pages[finger] = page;
 136   }
 137 }
 138 
 139 void ZRelocationSetSelectorGroup::select_inner() {
 140   // Calculate the number of pages to relocate by successively including pages in
 141   // a candidate relocation set and calculate the maximum space requirement for
 142   // their live objects.
 143   const size_t npages = _registered_pages.length();
 144   size_t selected_from = 0;
 145   size_t selected_to = 0;
 146   size_t from_size = 0;
 147 
 148   semi_sort();
 149 
 150   for (size_t from = 1; from <= npages; from++) {
 151     // Add page to the candidate relocation set
 152     from_size += _sorted_pages[from - 1]->live_bytes();
 153 
 154     // Calculate the maximum number of pages needed by the candidate relocation set.
 155     // By subtracting the object size limit from the pages size we get the maximum
 156     // number of pages that the relocation set is guaranteed to fit in, regardless
 157     // of in which order the objects are relocated.
 158     const size_t to = ceil((double)(from_size) / (double)(_page_size - _object_size_limit));
 159 
 160     // Calculate the relative difference in reclaimable space compared to our
 161     // currently selected final relocation set. If this number is larger than the
 162     // acceptable fragmentation limit, then the current candidate relocation set
 163     // becomes our new final relocation set.
 164     const size_t diff_from = from - selected_from;
 165     const size_t diff_to = to - selected_to;
 166     const double diff_reclaimable = 100 - percent_of(diff_to, diff_from);
 167     if (diff_reclaimable > ZFragmentationLimit) {
 168       selected_from = from;
 169       selected_to = to;
 170     }
 171 
 172     log_trace(gc, reloc)("Candidate Relocation Set (%s Pages): "
 173                          SIZE_FORMAT "->" SIZE_FORMAT ", %.1f%% relative defragmentation, %s",
 174                          _name, from, to, diff_reclaimable, (selected_from == from) ? "Selected" : "Rejected");
 175   }
 176 
 177   // Finalize selection
 178   _nselected = selected_from;
 179 
 180   // Update statistics
 181   _stats._compacting_from = selected_from * _page_size;
 182   _stats._compacting_to = selected_to * _page_size;
 183 
 184   log_trace(gc, reloc)("Relocation Set (%s Pages): " SIZE_FORMAT "->" SIZE_FORMAT ", " SIZE_FORMAT " skipped",
 185                        _name, selected_from, selected_to, npages - _nselected);
 186 }
 187 
 188 void ZRelocationSetSelectorGroup::select() {
 189   if (is_disabled()) {
 190     return;
 191   }
 192 
 193   EventZRelocationSetGroup event;
 194 
 195   if (is_selectable()) {
 196     select_inner();
 197   }
 198 
 199   // Send event
 200   event.commit(_page_type, _stats.npages(), _stats.total(), _stats.empty(),
 201                _stats.compacting_from(), _stats.compacting_to());
 202 }
 203 
 204 ZRelocationSetSelector::ZRelocationSetSelector() :
 205     _small("Small", ZPageTypeSmall, ZPageSizeSmall, ZObjectSizeLimitSmall),
 206     _medium("Medium", ZPageTypeMedium, ZPageSizeMedium, ZObjectSizeLimitMedium),
 207     _large("Large", ZPageTypeLarge, 0 /* page_size */, 0 /* object_size_limit */) {}
 208 
 209 void ZRelocationSetSelector::register_live_page(ZPage* page) {
 210   const uint8_t type = page->type();
 211 
 212   if (type == ZPageTypeSmall) {
 213     _small.register_live_page(page);
 214   } else if (type == ZPageTypeMedium) {
 215     _medium.register_live_page(page);
 216   } else {
 217     _large.register_live_page(page);
 218   }
 219 }
 220 
 221 void ZRelocationSetSelector::register_garbage_page(ZPage* page) {
 222   const uint8_t type = page->type();
 223 
 224   if (type == ZPageTypeSmall) {
 225     _small.register_garbage_page(page);
 226   } else if (type == ZPageTypeMedium) {
 227     _medium.register_garbage_page(page);
 228   } else {
 229     _large.register_garbage_page(page);
 230   }
 231 }
 232 
 233 void ZRelocationSetSelector::select(ZRelocationSet* relocation_set) {
 234   // Select pages to relocate. The resulting relocation set will be
 235   // sorted such that medium pages comes first, followed by small
 236   // pages. Pages within each page group will be semi-sorted by live
 237   // bytes in ascending order. Relocating pages in this order allows
 238   // us to start reclaiming memory more quickly.
 239 
 240   EventZRelocationSet event;
 241 
 242   // Select pages from each group
 243   _large.select();
 244   _medium.select();
 245   _small.select();
 246 
 247   // Populate relocation set
 248   relocation_set->populate(_medium.selected(), _medium.nselected(),
 249                            _small.selected(), _small.nselected());
 250 
 251   // Send event
 252   event.commit(total(), empty(), compacting_from(), compacting_to());
 253 }
 254 
 255 ZRelocationSetSelectorStats ZRelocationSetSelector::stats() const {
 256   ZRelocationSetSelectorStats stats;
 257   stats._small = _small.stats();
 258   stats._medium = _medium.stats();
 259   stats._large = _large.stats();
 260   return stats;
 261 }