src/share/vm/gc_implementation/g1/collectionSetChooser.cpp

Mon, 03 Aug 2009 12:59:30 -0700

author
johnc
date
Mon, 03 Aug 2009 12:59:30 -0700
changeset 1324
15c5903cf9e1
parent 1279
bd02caa94611
child 1538
9dc2adf2cbe0
permissions
-rw-r--r--

6865703: G1: Parallelize hot card cache cleanup
Summary: Have the GC worker threads clear the hot card cache in parallel by having each worker thread claim a chunk of the card cache and process the cards in that chunk. The size of the chunks that each thread will claim is determined at VM initialization from the size of the card cache and the number of worker threads.
Reviewed-by: jmasa, tonyp

ysr@777 1 /*
xdono@1279 2 * Copyright 2001-2009 Sun Microsystems, Inc. All Rights Reserved.
ysr@777 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
ysr@777 4 *
ysr@777 5 * This code is free software; you can redistribute it and/or modify it
ysr@777 6 * under the terms of the GNU General Public License version 2 only, as
ysr@777 7 * published by the Free Software Foundation.
ysr@777 8 *
ysr@777 9 * This code is distributed in the hope that it will be useful, but WITHOUT
ysr@777 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
ysr@777 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
ysr@777 12 * version 2 for more details (a copy is included in the LICENSE file that
ysr@777 13 * accompanied this code).
ysr@777 14 *
ysr@777 15 * You should have received a copy of the GNU General Public License version
ysr@777 16 * 2 along with this work; if not, write to the Free Software Foundation,
ysr@777 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
ysr@777 18 *
ysr@777 19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
ysr@777 20 * CA 95054 USA or visit www.sun.com if you need additional information or
ysr@777 21 * have any questions.
ysr@777 22 *
ysr@777 23 */
ysr@777 24
ysr@777 25 # include "incls/_precompiled.incl"
ysr@777 26 # include "incls/_collectionSetChooser.cpp.incl"
ysr@777 27
ysr@777 28 CSetChooserCache::CSetChooserCache() {
ysr@777 29 for (int i = 0; i < CacheLength; ++i)
ysr@777 30 _cache[i] = NULL;
ysr@777 31 clear();
ysr@777 32 }
ysr@777 33
ysr@777 34 void CSetChooserCache::clear() {
ysr@777 35 _occupancy = 0;
ysr@777 36 _first = 0;
ysr@777 37 for (int i = 0; i < CacheLength; ++i) {
ysr@777 38 HeapRegion *hr = _cache[i];
ysr@777 39 if (hr != NULL)
ysr@777 40 hr->set_sort_index(-1);
ysr@777 41 _cache[i] = NULL;
ysr@777 42 }
ysr@777 43 }
ysr@777 44
ysr@777 45 #ifndef PRODUCT
ysr@777 46 bool CSetChooserCache::verify() {
ysr@777 47 int index = _first;
ysr@777 48 HeapRegion *prev = NULL;
ysr@777 49 for (int i = 0; i < _occupancy; ++i) {
ysr@777 50 guarantee(_cache[index] != NULL, "cache entry should not be empty");
ysr@777 51 HeapRegion *hr = _cache[index];
ysr@777 52 guarantee(!hr->is_young(), "should not be young!");
ysr@777 53 if (prev != NULL) {
ysr@777 54 guarantee(prev->gc_efficiency() >= hr->gc_efficiency(),
ysr@777 55 "cache should be correctly ordered");
ysr@777 56 }
ysr@777 57 guarantee(hr->sort_index() == get_sort_index(index),
ysr@777 58 "sort index should be correct");
ysr@777 59 index = trim_index(index + 1);
ysr@777 60 prev = hr;
ysr@777 61 }
ysr@777 62
ysr@777 63 for (int i = 0; i < (CacheLength - _occupancy); ++i) {
ysr@777 64 guarantee(_cache[index] == NULL, "cache entry should be empty");
ysr@777 65 index = trim_index(index + 1);
ysr@777 66 }
ysr@777 67
ysr@777 68 guarantee(index == _first, "we should have reached where we started from");
ysr@777 69 return true;
ysr@777 70 }
ysr@777 71 #endif // PRODUCT
ysr@777 72
ysr@777 73 void CSetChooserCache::insert(HeapRegion *hr) {
ysr@777 74 assert(!is_full(), "cache should not be empty");
ysr@777 75 hr->calc_gc_efficiency();
ysr@777 76
ysr@777 77 int empty_index;
ysr@777 78 if (_occupancy == 0) {
ysr@777 79 empty_index = _first;
ysr@777 80 } else {
ysr@777 81 empty_index = trim_index(_first + _occupancy);
ysr@777 82 assert(_cache[empty_index] == NULL, "last slot should be empty");
ysr@777 83 int last_index = trim_index(empty_index - 1);
ysr@777 84 HeapRegion *last = _cache[last_index];
ysr@777 85 assert(last != NULL,"as the cache is not empty, last should not be empty");
ysr@777 86 while (empty_index != _first &&
ysr@777 87 last->gc_efficiency() < hr->gc_efficiency()) {
ysr@777 88 _cache[empty_index] = last;
ysr@777 89 last->set_sort_index(get_sort_index(empty_index));
ysr@777 90 empty_index = last_index;
ysr@777 91 last_index = trim_index(last_index - 1);
ysr@777 92 last = _cache[last_index];
ysr@777 93 }
ysr@777 94 }
ysr@777 95 _cache[empty_index] = hr;
ysr@777 96 hr->set_sort_index(get_sort_index(empty_index));
ysr@777 97
ysr@777 98 ++_occupancy;
ysr@777 99 assert(verify(), "cache should be consistent");
ysr@777 100 }
ysr@777 101
ysr@777 102 HeapRegion *CSetChooserCache::remove_first() {
ysr@777 103 if (_occupancy > 0) {
ysr@777 104 assert(_cache[_first] != NULL, "cache should have at least one region");
ysr@777 105 HeapRegion *ret = _cache[_first];
ysr@777 106 _cache[_first] = NULL;
ysr@777 107 ret->set_sort_index(-1);
ysr@777 108 --_occupancy;
ysr@777 109 _first = trim_index(_first + 1);
ysr@777 110 assert(verify(), "cache should be consistent");
ysr@777 111 return ret;
ysr@777 112 } else {
ysr@777 113 return NULL;
ysr@777 114 }
ysr@777 115 }
ysr@777 116
ysr@777 117 // this is a bit expensive... but we expect that it should not be called
ysr@777 118 // to often.
ysr@777 119 void CSetChooserCache::remove(HeapRegion *hr) {
ysr@777 120 assert(_occupancy > 0, "cache should not be empty");
ysr@777 121 assert(hr->sort_index() < -1, "should already be in the cache");
ysr@777 122 int index = get_index(hr->sort_index());
ysr@777 123 assert(_cache[index] == hr, "index should be correct");
ysr@777 124 int next_index = trim_index(index + 1);
ysr@777 125 int last_index = trim_index(_first + _occupancy - 1);
ysr@777 126 while (index != last_index) {
ysr@777 127 assert(_cache[next_index] != NULL, "should not be null");
ysr@777 128 _cache[index] = _cache[next_index];
ysr@777 129 _cache[index]->set_sort_index(get_sort_index(index));
ysr@777 130
ysr@777 131 index = next_index;
ysr@777 132 next_index = trim_index(next_index+1);
ysr@777 133 }
ysr@777 134 assert(index == last_index, "should have reached the last one");
ysr@777 135 _cache[index] = NULL;
ysr@777 136 hr->set_sort_index(-1);
ysr@777 137 --_occupancy;
ysr@777 138 assert(verify(), "cache should be consistent");
ysr@777 139 }
ysr@777 140
ysr@777 141 static inline int orderRegions(HeapRegion* hr1, HeapRegion* hr2) {
ysr@777 142 if (hr1 == NULL) {
ysr@777 143 if (hr2 == NULL) return 0;
ysr@777 144 else return 1;
ysr@777 145 } else if (hr2 == NULL) {
ysr@777 146 return -1;
ysr@777 147 }
ysr@777 148 if (hr2->gc_efficiency() < hr1->gc_efficiency()) return -1;
ysr@777 149 else if (hr1->gc_efficiency() < hr2->gc_efficiency()) return 1;
ysr@777 150 else return 0;
ysr@777 151 }
ysr@777 152
ysr@777 153 static int orderRegions(HeapRegion** hr1p, HeapRegion** hr2p) {
ysr@777 154 return orderRegions(*hr1p, *hr2p);
ysr@777 155 }
ysr@777 156
ysr@777 157 CollectionSetChooser::CollectionSetChooser() :
ysr@777 158 // The line below is the worst bit of C++ hackery I've ever written
ysr@777 159 // (Detlefs, 11/23). You should think of it as equivalent to
ysr@777 160 // "_regions(100, true)": initialize the growable array and inform it
ysr@777 161 // that it should allocate its elem array(s) on the C heap. The first
ysr@777 162 // argument, however, is actually a comma expression (new-expr, 100).
ysr@777 163 // The purpose of the new_expr is to inform the growable array that it
ysr@777 164 // is *already* allocated on the C heap: it uses the placement syntax to
ysr@777 165 // keep it from actually doing any allocation.
ysr@777 166 _markedRegions((ResourceObj::operator new (sizeof(GrowableArray<HeapRegion*>),
ysr@777 167 (void*)&_markedRegions,
ysr@777 168 ResourceObj::C_HEAP),
ysr@777 169 100),
ysr@777 170 true),
ysr@777 171 _curMarkedIndex(0),
ysr@777 172 _numMarkedRegions(0),
ysr@777 173 _unmarked_age_1_returned_as_new(false),
ysr@777 174 _first_par_unreserved_idx(0)
ysr@777 175 {}
ysr@777 176
ysr@777 177
ysr@777 178
ysr@777 179 #ifndef PRODUCT
ysr@777 180 bool CollectionSetChooser::verify() {
ysr@777 181 int index = 0;
ysr@777 182 guarantee(_curMarkedIndex <= _numMarkedRegions,
ysr@777 183 "_curMarkedIndex should be within bounds");
ysr@777 184 while (index < _curMarkedIndex) {
ysr@777 185 guarantee(_markedRegions.at(index++) == NULL,
ysr@777 186 "all entries before _curMarkedIndex should be NULL");
ysr@777 187 }
ysr@777 188 HeapRegion *prev = NULL;
ysr@777 189 while (index < _numMarkedRegions) {
ysr@777 190 HeapRegion *curr = _markedRegions.at(index++);
ysr@777 191 if (curr != NULL) {
ysr@777 192 int si = curr->sort_index();
ysr@777 193 guarantee(!curr->is_young(), "should not be young!");
ysr@777 194 guarantee(si > -1 && si == (index-1), "sort index invariant");
ysr@777 195 if (prev != NULL) {
ysr@777 196 guarantee(orderRegions(prev, curr) != 1, "regions should be sorted");
ysr@777 197 }
ysr@777 198 prev = curr;
ysr@777 199 }
ysr@777 200 }
ysr@777 201 return _cache.verify();
ysr@777 202 }
ysr@777 203 #endif
ysr@777 204
ysr@777 205 bool
ysr@777 206 CollectionSetChooser::addRegionToCache() {
ysr@777 207 assert(!_cache.is_full(), "cache should not be full");
ysr@777 208
ysr@777 209 HeapRegion *hr = NULL;
ysr@777 210 while (hr == NULL && _curMarkedIndex < _numMarkedRegions) {
ysr@777 211 hr = _markedRegions.at(_curMarkedIndex++);
ysr@777 212 }
ysr@777 213 if (hr == NULL)
ysr@777 214 return false;
ysr@777 215 assert(!hr->is_young(), "should not be young!");
ysr@777 216 assert(hr->sort_index() == _curMarkedIndex-1, "sort_index invariant");
ysr@777 217 _markedRegions.at_put(hr->sort_index(), NULL);
ysr@777 218 _cache.insert(hr);
ysr@777 219 assert(!_cache.is_empty(), "cache should not be empty");
ysr@777 220 assert(verify(), "cache should be consistent");
ysr@777 221 return false;
ysr@777 222 }
ysr@777 223
ysr@777 224 void
ysr@777 225 CollectionSetChooser::fillCache() {
ysr@777 226 while (!_cache.is_full() && addRegionToCache()) {
ysr@777 227 }
ysr@777 228 }
ysr@777 229
ysr@777 230 void
ysr@777 231 CollectionSetChooser::sortMarkedHeapRegions() {
ysr@777 232 guarantee(_cache.is_empty(), "cache should be empty");
ysr@777 233 // First trim any unused portion of the top in the parallel case.
ysr@777 234 if (_first_par_unreserved_idx > 0) {
ysr@777 235 if (G1PrintParCleanupStats) {
ysr@777 236 gclog_or_tty->print(" Truncating _markedRegions from %d to %d.\n",
ysr@777 237 _markedRegions.length(), _first_par_unreserved_idx);
ysr@777 238 }
ysr@777 239 assert(_first_par_unreserved_idx <= _markedRegions.length(),
ysr@777 240 "Or we didn't reserved enough length");
ysr@777 241 _markedRegions.trunc_to(_first_par_unreserved_idx);
ysr@777 242 }
ysr@777 243 _markedRegions.sort(orderRegions);
ysr@777 244 assert(_numMarkedRegions <= _markedRegions.length(), "Requirement");
ysr@777 245 assert(_numMarkedRegions == 0
ysr@777 246 || _markedRegions.at(_numMarkedRegions-1) != NULL,
ysr@777 247 "Testing _numMarkedRegions");
ysr@777 248 assert(_numMarkedRegions == _markedRegions.length()
ysr@777 249 || _markedRegions.at(_numMarkedRegions) == NULL,
ysr@777 250 "Testing _numMarkedRegions");
ysr@777 251 if (G1PrintParCleanupStats) {
ysr@777 252 gclog_or_tty->print_cr(" Sorted %d marked regions.", _numMarkedRegions);
ysr@777 253 }
ysr@777 254 for (int i = 0; i < _numMarkedRegions; i++) {
ysr@777 255 assert(_markedRegions.at(i) != NULL, "Should be true by sorting!");
ysr@777 256 _markedRegions.at(i)->set_sort_index(i);
ysr@777 257 if (G1PrintRegionLivenessInfo > 0) {
ysr@777 258 if (i == 0) gclog_or_tty->print_cr("Sorted marked regions:");
ysr@777 259 if (i < G1PrintRegionLivenessInfo ||
ysr@777 260 (_numMarkedRegions-i) < G1PrintRegionLivenessInfo) {
ysr@777 261 HeapRegion* hr = _markedRegions.at(i);
ysr@777 262 size_t u = hr->used();
ysr@777 263 gclog_or_tty->print_cr(" Region %d: %d used, %d max live, %5.2f%%.",
ysr@777 264 i, u, hr->max_live_bytes(),
ysr@777 265 100.0*(float)hr->max_live_bytes()/(float)u);
ysr@777 266 }
ysr@777 267 }
ysr@777 268 }
ysr@777 269 if (G1PolicyVerbose > 1)
ysr@777 270 printSortedHeapRegions();
ysr@777 271 assert(verify(), "should now be sorted");
ysr@777 272 }
ysr@777 273
ysr@777 274 void
ysr@777 275 printHeapRegion(HeapRegion *hr) {
ysr@777 276 if (hr->isHumongous())
ysr@777 277 gclog_or_tty->print("H: ");
ysr@777 278 if (hr->in_collection_set())
ysr@777 279 gclog_or_tty->print("CS: ");
ysr@777 280 gclog_or_tty->print_cr("Region " PTR_FORMAT " (%s%s) "
ysr@777 281 "[" PTR_FORMAT ", " PTR_FORMAT"] "
ysr@777 282 "Used: " SIZE_FORMAT "K, garbage: " SIZE_FORMAT "K.",
ysr@777 283 hr, hr->is_young() ? "Y " : " ",
ysr@777 284 hr->is_marked()? "M1" : "M0",
ysr@777 285 hr->bottom(), hr->end(),
ysr@777 286 hr->used()/K, hr->garbage_bytes()/K);
ysr@777 287 }
ysr@777 288
ysr@777 289 void
ysr@777 290 CollectionSetChooser::addMarkedHeapRegion(HeapRegion* hr) {
ysr@777 291 assert(!hr->isHumongous(),
ysr@777 292 "Humongous regions shouldn't be added to the collection set");
ysr@777 293 assert(!hr->is_young(), "should not be young!");
ysr@777 294 _markedRegions.append(hr);
ysr@777 295 _numMarkedRegions++;
ysr@777 296 hr->calc_gc_efficiency();
ysr@777 297 }
ysr@777 298
ysr@777 299 void
ysr@777 300 CollectionSetChooser::
ysr@777 301 prepareForAddMarkedHeapRegionsPar(size_t n_regions, size_t chunkSize) {
ysr@777 302 _first_par_unreserved_idx = 0;
ysr@777 303 size_t max_waste = ParallelGCThreads * chunkSize;
ysr@777 304 // it should be aligned with respect to chunkSize
ysr@777 305 size_t aligned_n_regions =
ysr@777 306 (n_regions + (chunkSize - 1)) / chunkSize * chunkSize;
ysr@777 307 assert( aligned_n_regions % chunkSize == 0, "should be aligned" );
ysr@777 308 _markedRegions.at_put_grow((int)(aligned_n_regions + max_waste - 1), NULL);
ysr@777 309 }
ysr@777 310
ysr@777 311 jint
ysr@777 312 CollectionSetChooser::getParMarkedHeapRegionChunk(jint n_regions) {
ysr@777 313 jint res = Atomic::add(n_regions, &_first_par_unreserved_idx);
ysr@777 314 assert(_markedRegions.length() > res + n_regions - 1,
ysr@777 315 "Should already have been expanded");
ysr@777 316 return res - n_regions;
ysr@777 317 }
ysr@777 318
ysr@777 319 void
ysr@777 320 CollectionSetChooser::setMarkedHeapRegion(jint index, HeapRegion* hr) {
ysr@777 321 assert(_markedRegions.at(index) == NULL, "precondition");
ysr@777 322 assert(!hr->is_young(), "should not be young!");
ysr@777 323 _markedRegions.at_put(index, hr);
ysr@777 324 hr->calc_gc_efficiency();
ysr@777 325 }
ysr@777 326
ysr@777 327 void
ysr@777 328 CollectionSetChooser::incNumMarkedHeapRegions(jint inc_by) {
ysr@777 329 (void)Atomic::add(inc_by, &_numMarkedRegions);
ysr@777 330 }
ysr@777 331
ysr@777 332 void
ysr@777 333 CollectionSetChooser::clearMarkedHeapRegions(){
ysr@777 334 for (int i = 0; i < _markedRegions.length(); i++) {
ysr@777 335 HeapRegion* r = _markedRegions.at(i);
ysr@777 336 if (r != NULL) r->set_sort_index(-1);
ysr@777 337 }
ysr@777 338 _markedRegions.clear();
ysr@777 339 _curMarkedIndex = 0;
ysr@777 340 _numMarkedRegions = 0;
ysr@777 341 _cache.clear();
ysr@777 342 };
ysr@777 343
ysr@777 344 void
ysr@777 345 CollectionSetChooser::updateAfterFullCollection() {
ysr@777 346 G1CollectedHeap* g1h = G1CollectedHeap::heap();
ysr@777 347 clearMarkedHeapRegions();
ysr@777 348 }
ysr@777 349
ysr@777 350 void
ysr@777 351 CollectionSetChooser::printSortedHeapRegions() {
ysr@777 352 gclog_or_tty->print_cr("Printing %d Heap Regions sorted by amount of known garbage",
ysr@777 353 _numMarkedRegions);
ysr@777 354 for (int i = 0; i < _markedRegions.length(); i++) {
ysr@777 355 printHeapRegion(_markedRegions.at(i));
ysr@777 356 }
ysr@777 357 gclog_or_tty->print_cr("Done sorted heap region print");
ysr@777 358 }
ysr@777 359
ysr@777 360 void CollectionSetChooser::removeRegion(HeapRegion *hr) {
ysr@777 361 int si = hr->sort_index();
ysr@777 362 assert(si == -1 || hr->is_marked(), "Sort index not valid.");
ysr@777 363 if (si > -1) {
ysr@777 364 assert(_markedRegions.at(si) == hr, "Sort index not valid." );
ysr@777 365 _markedRegions.at_put(si, NULL);
ysr@777 366 } else if (si < -1) {
ysr@777 367 assert(_cache.region_in_cache(hr), "should be in the cache");
ysr@777 368 _cache.remove(hr);
ysr@777 369 assert(hr->sort_index() == -1, "sort index invariant");
ysr@777 370 }
ysr@777 371 hr->set_sort_index(-1);
ysr@777 372 }
ysr@777 373
ysr@777 374 // if time_remaining < 0.0, then this method should try to return
ysr@777 375 // a region, whether it fits within the remaining time or not
ysr@777 376 HeapRegion*
ysr@777 377 CollectionSetChooser::getNextMarkedRegion(double time_remaining,
ysr@777 378 double avg_prediction) {
ysr@777 379 G1CollectedHeap* g1h = G1CollectedHeap::heap();
ysr@777 380 G1CollectorPolicy* g1p = g1h->g1_policy();
ysr@777 381 fillCache();
ysr@777 382 if (_cache.is_empty()) {
ysr@777 383 assert(_curMarkedIndex == _numMarkedRegions,
ysr@777 384 "if cache is empty, list should also be empty");
ysr@777 385 return NULL;
ysr@777 386 }
ysr@777 387
ysr@777 388 HeapRegion *hr = _cache.get_first();
ysr@777 389 assert(hr != NULL, "if cache not empty, first entry should be non-null");
ysr@777 390 double predicted_time = g1h->predict_region_elapsed_time_ms(hr, false);
ysr@777 391
ysr@777 392 if (g1p->adaptive_young_list_length()) {
ysr@777 393 if (time_remaining - predicted_time < 0.0) {
ysr@777 394 g1h->check_if_region_is_too_expensive(predicted_time);
ysr@777 395 return NULL;
ysr@777 396 }
ysr@777 397 } else {
ysr@777 398 if (predicted_time > 2.0 * avg_prediction) {
ysr@777 399 return NULL;
ysr@777 400 }
ysr@777 401 }
ysr@777 402
ysr@777 403 HeapRegion *hr2 = _cache.remove_first();
ysr@777 404 assert(hr == hr2, "cache contents should not have changed");
ysr@777 405
ysr@777 406 return hr;
ysr@777 407 }

mercurial