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