1 /* 2 * Copyright Amazon.com Inc. 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 25 #include "precompiled.hpp" 26 27 #include "gc/shenandoah/mode/shenandoahGenerationalMode.hpp" 28 #include "gc/shenandoah/shenandoahAgeCensus.hpp" 29 #include "gc/shenandoah/shenandoahHeap.inline.hpp" 30 31 ShenandoahAgeCensus::ShenandoahAgeCensus() { 32 assert(ShenandoahHeap::heap()->mode()->is_generational(), "Only in generational mode"); 33 34 _global_age_table = NEW_C_HEAP_ARRAY(AgeTable*, MAX_SNAPSHOTS, mtGC); 35 CENSUS_NOISE(_global_noise = NEW_C_HEAP_ARRAY(ShenandoahNoiseStats, MAX_SNAPSHOTS, mtGC);) 36 _tenuring_threshold = NEW_C_HEAP_ARRAY(uint, MAX_SNAPSHOTS, mtGC); 37 38 for (int i = 0; i < MAX_SNAPSHOTS; i++) { 39 // Note that we don't now get perfdata from age_table 40 _global_age_table[i] = new AgeTable(false); 41 CENSUS_NOISE(_global_noise[i].clear();) 42 // Sentinel value 43 _tenuring_threshold[i] = MAX_COHORTS; 44 } 45 if (ShenandoahGenerationalAdaptiveTenuring && !ShenandoahGenerationalCensusAtEvac) { 46 size_t max_workers = ShenandoahHeap::heap()->max_workers(); 47 _local_age_table = NEW_C_HEAP_ARRAY(AgeTable*, max_workers, mtGC); 48 CENSUS_NOISE(_local_noise = NEW_C_HEAP_ARRAY(ShenandoahNoiseStats, max_workers, mtGC);) 49 for (uint i = 0; i < max_workers; i++) { 50 _local_age_table[i] = new AgeTable(false); 51 CENSUS_NOISE(_local_noise[i].clear();) 52 } 53 } else { 54 _local_age_table = nullptr; 55 } 56 _epoch = MAX_SNAPSHOTS - 1; // see update_epoch() 57 } 58 59 CENSUS_NOISE(void ShenandoahAgeCensus::add(uint obj_age, uint region_age, uint region_youth, size_t size, uint worker_id) {) 60 NO_CENSUS_NOISE(void ShenandoahAgeCensus::add(uint obj_age, uint region_age, size_t size, uint worker_id) {) 61 if (obj_age <= markWord::max_age) { 62 assert(obj_age < MAX_COHORTS && region_age < MAX_COHORTS, "Should have been tenured"); 63 #ifdef SHENANDOAH_CENSUS_NOISE 64 // Region ageing is stochastic and non-monotonic; this vitiates mortality 65 // demographics in ways that might defeat our algorithms. Marking may be a 66 // time when we might be able to correct this, but we currently do not do 67 // this. Like skipped statistics further below, we want to track the 68 // impact of this noise to see if this may be worthwhile. JDK-<TBD>. 69 uint age = obj_age; 70 if (region_age > 0) { 71 add_aged(size, worker_id); // this tracking is coarse for now 72 age += region_age; 73 if (age >= MAX_COHORTS) { 74 age = (uint)(MAX_COHORTS - 1); // clamp 75 add_clamped(size, worker_id); 76 } 77 } 78 if (region_youth > 0) { // track object volume with retrograde age 79 add_young(size, worker_id); 80 } 81 #else // SHENANDOAH_CENSUS_NOISE 82 uint age = MIN2(obj_age + region_age, (uint)(MAX_COHORTS - 1)); // clamp 83 #endif // SHENANDOAH_CENSUS_NOISE 84 get_local_age_table(worker_id)->add(age, size); 85 } else { 86 // update skipped statistics 87 CENSUS_NOISE(add_skipped(size, worker_id);) 88 } 89 } 90 91 #ifdef SHENANDOAH_CENSUS_NOISE 92 void ShenandoahAgeCensus::add_skipped(size_t size, uint worker_id) { 93 _local_noise[worker_id].skipped += size; 94 } 95 96 void ShenandoahAgeCensus::add_aged(size_t size, uint worker_id) { 97 _local_noise[worker_id].aged += size; 98 } 99 100 void ShenandoahAgeCensus::add_clamped(size_t size, uint worker_id) { 101 _local_noise[worker_id].clamped += size; 102 } 103 104 void ShenandoahAgeCensus::add_young(size_t size, uint worker_id) { 105 _local_noise[worker_id].young += size; 106 } 107 #endif // SHENANDOAH_CENSUS_NOISE 108 109 // Prepare for a new census update, by clearing appropriate global slots. 110 void ShenandoahAgeCensus::prepare_for_census_update() { 111 assert(_epoch < MAX_SNAPSHOTS, "Out of bounds"); 112 if (++_epoch >= MAX_SNAPSHOTS) { 113 _epoch=0; 114 } 115 _global_age_table[_epoch]->clear(); 116 CENSUS_NOISE(_global_noise[_epoch].clear();) 117 } 118 119 // Update the census data from appropriate sources, 120 // and compute the new tenuring threshold. 121 void ShenandoahAgeCensus::update_census(size_t age0_pop, AgeTable* pv1, AgeTable* pv2) { 122 // Check that we won't overwrite existing data: caller is 123 // responsible for explicitly clearing the slot via calling 124 // prepare_for_census_update(). 125 assert(_global_age_table[_epoch]->is_clear(), "Dirty decks"); 126 CENSUS_NOISE(assert(_global_noise[_epoch].is_clear(), "Dirty decks");) 127 if (ShenandoahGenerationalAdaptiveTenuring && !ShenandoahGenerationalCensusAtEvac) { 128 assert(pv1 == nullptr && pv2 == nullptr, "Error, check caller"); 129 // Seed cohort 0 with population that may have been missed during 130 // regular census. 131 _global_age_table[_epoch]->add((uint)0, age0_pop); 132 133 size_t max_workers = ShenandoahHeap::heap()->max_workers(); 134 // Merge data from local age tables into the global age table for the epoch, 135 // clearing the local tables. 136 for (uint i = 0; i < max_workers; i++) { 137 // age stats 138 _global_age_table[_epoch]->merge(_local_age_table[i]); 139 _local_age_table[i]->clear(); // clear for next census 140 // Merge noise stats 141 CENSUS_NOISE(_global_noise[_epoch].merge(_local_noise[i]);) 142 CENSUS_NOISE(_local_noise[i].clear();) 143 } 144 } else { 145 // census during evac 146 assert(pv1 != nullptr && pv2 != nullptr, "Error, check caller"); 147 _global_age_table[_epoch]->merge(pv1); 148 _global_age_table[_epoch]->merge(pv2); 149 } 150 151 update_tenuring_threshold(); 152 } 153 154 155 // Reset the epoch for the global age tables, 156 // clearing all history. 157 void ShenandoahAgeCensus::reset_global() { 158 assert(_epoch < MAX_SNAPSHOTS, "Out of bounds"); 159 for (uint i = 0; i < MAX_SNAPSHOTS; i++) { 160 _global_age_table[i]->clear(); 161 CENSUS_NOISE(_global_noise[i].clear();) 162 } 163 _epoch = MAX_SNAPSHOTS; 164 assert(_epoch < MAX_SNAPSHOTS, "Error"); 165 } 166 167 // Reset the local age tables, clearing any partial census. 168 void ShenandoahAgeCensus::reset_local() { 169 if (!ShenandoahGenerationalAdaptiveTenuring || ShenandoahGenerationalCensusAtEvac) { 170 assert(_local_age_table == nullptr, "Error"); 171 return; 172 } 173 size_t max_workers = ShenandoahHeap::heap()->max_workers(); 174 for (uint i = 0; i < max_workers; i++) { 175 _local_age_table[i]->clear(); 176 CENSUS_NOISE(_local_noise[i].clear();) 177 } 178 } 179 180 // Is global census information clear? 181 bool ShenandoahAgeCensus::is_clear_global() { 182 assert(_epoch < MAX_SNAPSHOTS, "Out of bounds"); 183 for (uint i = 0; i < MAX_SNAPSHOTS; i++) { 184 bool clear = _global_age_table[i]->is_clear(); 185 CENSUS_NOISE(clear |= _global_noise[i].is_clear();) 186 if (!clear) { 187 return false; 188 } 189 } 190 return true; 191 } 192 193 // Is local census information clear? 194 bool ShenandoahAgeCensus::is_clear_local() { 195 if (!ShenandoahGenerationalAdaptiveTenuring || ShenandoahGenerationalCensusAtEvac) { 196 assert(_local_age_table == nullptr, "Error"); 197 return true; 198 } 199 size_t max_workers = ShenandoahHeap::heap()->max_workers(); 200 for (uint i = 0; i < max_workers; i++) { 201 bool clear = _local_age_table[i]->is_clear(); 202 CENSUS_NOISE(clear |= _local_noise[i].is_clear();) 203 if (!clear) { 204 return false; 205 } 206 } 207 return true; 208 } 209 210 void ShenandoahAgeCensus::update_tenuring_threshold() { 211 if (!ShenandoahGenerationalAdaptiveTenuring) { 212 _tenuring_threshold[_epoch] = InitialTenuringThreshold; 213 } else { 214 uint tt = compute_tenuring_threshold(); 215 assert(tt <= MAX_COHORTS, "Out of bounds"); 216 _tenuring_threshold[_epoch] = tt; 217 } 218 print(); 219 log_trace(gc, age)("New tenuring threshold " UINTX_FORMAT " (min " UINTX_FORMAT ", max " UINTX_FORMAT")", 220 (uintx) _tenuring_threshold[_epoch], ShenandoahGenerationalMinTenuringAge, ShenandoahGenerationalMaxTenuringAge); 221 } 222 223 uint ShenandoahAgeCensus::compute_tenuring_threshold() { 224 // Starting with the oldest cohort with a non-trivial population 225 // (as specified by ShenandoahGenerationalTenuringCohortPopulationThreshold) in the 226 // previous epoch, and working down the cohorts by age, find the 227 // oldest age that has a significant mortality rate (as specified by 228 // ShenandoahGenerationalTenuringMortalityRateThreshold). We use this as 229 // tenuring age to be used for the evacuation cycle to follow. 230 // Results are clamped between user-specified min & max guardrails, 231 // so we ignore any cohorts outside ShenandoahGenerational[Min,Max]Age. 232 233 // Current and previous epoch in ring 234 const uint cur_epoch = _epoch; 235 const uint prev_epoch = cur_epoch > 0 ? cur_epoch - 1 : markWord::max_age; 236 237 // Current and previous population vectors in ring 238 const AgeTable* cur_pv = _global_age_table[cur_epoch]; 239 const AgeTable* prev_pv = _global_age_table[prev_epoch]; 240 uint upper_bound = ShenandoahGenerationalMaxTenuringAge - 1; 241 const uint prev_tt = previous_tenuring_threshold(); 242 if (ShenandoahGenerationalCensusIgnoreOlderCohorts && prev_tt > 0) { 243 // We stay below the computed tenuring threshold for the last cycle plus 1, 244 // ignoring the mortality rates of any older cohorts. 245 upper_bound = MIN2(upper_bound, prev_tt + 1); 246 } 247 uint tenuring_threshold = upper_bound; 248 for (uint i = upper_bound; i > MAX2((uint)ShenandoahGenerationalMinTenuringAge, (uint)0); i--) { 249 assert(i > 0, "Index (i-1) would underflow/wrap"); 250 // Cohort of current age i 251 const size_t cur_pop = cur_pv->sizes[i]; 252 const size_t prev_pop = prev_pv->sizes[i-1]; 253 const double mr = mortality_rate(prev_pop, cur_pop); 254 // We ignore any cohorts that had a very low population count, or 255 // that have a lower mortality rate than we care to age in young; these 256 // cohorts are considered eligible for tenuring when all older 257 // cohorts are. 258 if (prev_pop < ShenandoahGenerationalTenuringCohortPopulationThreshold || 259 mr < ShenandoahGenerationalTenuringMortalityRateThreshold) { 260 tenuring_threshold = i; 261 continue; 262 } 263 return tenuring_threshold; 264 } 265 return tenuring_threshold; 266 } 267 268 // Mortality rate of a cohort, given its previous and current population 269 double ShenandoahAgeCensus::mortality_rate(size_t prev_pop, size_t cur_pop) { 270 // The following also covers the case where both entries are 0 271 if (prev_pop <= cur_pop) { 272 // adjust for inaccurate censuses by finessing the 273 // reappearance of dark matter as normal matter; 274 // mortality rate is 0 if population remained the same 275 // or increased. 276 if (cur_pop > prev_pop) { 277 log_trace(gc, age) 278 (" (dark matter) Cohort population " SIZE_FORMAT_W(10) " to " SIZE_FORMAT_W(10), 279 prev_pop*oopSize, cur_pop*oopSize); 280 } 281 return 0.0; 282 } 283 assert(prev_pop > 0 && prev_pop > cur_pop, "Error"); 284 return 1.0 - (((double)cur_pop)/((double)prev_pop)); 285 } 286 287 void ShenandoahAgeCensus::print() { 288 // Print the population vector for the current epoch, and 289 // for the previous epoch, as well as the computed mortality 290 // ratio for each extant cohort. 291 const uint cur_epoch = _epoch; 292 const uint prev_epoch = cur_epoch > 0 ? cur_epoch - 1: markWord::max_age; 293 294 const AgeTable* cur_pv = _global_age_table[cur_epoch]; 295 const AgeTable* prev_pv = _global_age_table[prev_epoch]; 296 297 const uint tt = tenuring_threshold(); 298 299 size_t total= 0; 300 for (uint i = 1; i < MAX_COHORTS; i++) { 301 const size_t prev_pop = prev_pv->sizes[i-1]; // (i-1) OK because i >= 1 302 const size_t cur_pop = cur_pv->sizes[i]; 303 double mr = mortality_rate(prev_pop, cur_pop); 304 // Suppress printing when everything is zero 305 if (prev_pop + cur_pop > 0) { 306 log_info(gc, age) 307 (" - age %3u: prev " SIZE_FORMAT_W(10) " bytes, curr " SIZE_FORMAT_W(10) " bytes, mortality %.2f ", 308 i, prev_pop*oopSize, cur_pop*oopSize, mr); 309 } 310 total += cur_pop; 311 if (i == tt) { 312 // Underline the cohort for tenuring threshold (if < MAX_COHORTS) 313 log_info(gc, age)("----------------------------------------------------------------------------"); 314 } 315 } 316 CENSUS_NOISE(_global_noise[cur_epoch].print(total);) 317 } 318 319 #ifdef SHENANDOAH_CENSUS_NOISE 320 void ShenandoahNoiseStats::print(size_t total) { 321 if (total > 0) { 322 float f_skipped = (float)skipped/(float)total; 323 float f_aged = (float)aged/(float)total; 324 float f_clamped = (float)clamped/(float)total; 325 float f_young = (float)young/(float)total; 326 log_info(gc, age)("Skipped: " SIZE_FORMAT_W(10) " (%.2f), R-Aged: " SIZE_FORMAT_W(10) " (%.2f), " 327 "Clamped: " SIZE_FORMAT_W(10) " (%.2f), R-Young: " SIZE_FORMAT_W(10) " (%.2f)", 328 skipped*oopSize, f_skipped, aged*oopSize, f_aged, 329 clamped*oopSize, f_clamped, young*oopSize, f_young); 330 } 331 } 332 #endif // SHENANDOAH_CENSUS_NOISE