src/share/vm/gc_implementation/concurrentMarkSweep/promotionInfo.cpp

Mon, 16 Aug 2010 15:58:42 -0700

author
ysr
date
Mon, 16 Aug 2010 15:58:42 -0700
changeset 2071
be3f9c242c9d
parent 1907
c18cbe5936b8
child 2132
179464550c7d
permissions
-rw-r--r--

6948538: CMS: BOT walkers can fall into object allocation and initialization cracks
Summary: GC workers now recognize an intermediate transient state of blocks which are allocated but have not yet completed initialization. blk_start() calls do not attempt to determine the size of a block in the transient state, rather waiting for the block to become initialized so that it is safe to query its size. Audited and ensured the order of initialization of object fields (klass, free bit and size) to respect block state transition protocol. Also included some new assertion checking code enabled in debug mode.
Reviewed-by: chrisphi, johnc, poonam

ysr@1876 1 /*
trims@1907 2 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
ysr@1876 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
ysr@1876 4 *
ysr@1876 5 * This code is free software; you can redistribute it and/or modify it
ysr@1876 6 * under the terms of the GNU General Public License version 2 only, as
ysr@1876 7 * published by the Free Software Foundation.
ysr@1876 8 *
ysr@1876 9 * This code is distributed in the hope that it will be useful, but WITHOUT
ysr@1876 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
ysr@1876 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
ysr@1876 12 * version 2 for more details (a copy is included in the LICENSE file that
ysr@1876 13 * accompanied this code).
ysr@1876 14 *
ysr@1876 15 * You should have received a copy of the GNU General Public License version
ysr@1876 16 * 2 along with this work; if not, write to the Free Software Foundation,
ysr@1876 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
ysr@1876 18 *
trims@1907 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
trims@1907 20 * or visit www.oracle.com if you need additional information or have any
trims@1907 21 * questions.
ysr@1876 22 *
ysr@1876 23 */
ysr@1876 24
ysr@1876 25 # include "incls/_precompiled.incl"
ysr@1876 26 # include "incls/_promotionInfo.cpp.incl"
ysr@1876 27
ysr@1876 28 /////////////////////////////////////////////////////////////////////////
ysr@1876 29 //// PromotionInfo
ysr@1876 30 /////////////////////////////////////////////////////////////////////////
ysr@1876 31
ysr@1876 32
ysr@1876 33 //////////////////////////////////////////////////////////////////////////////
ysr@1876 34 // We go over the list of promoted objects, removing each from the list,
ysr@1876 35 // and applying the closure (this may, in turn, add more elements to
ysr@1876 36 // the tail of the promoted list, and these newly added objects will
ysr@1876 37 // also be processed) until the list is empty.
ysr@1876 38 // To aid verification and debugging, in the non-product builds
ysr@1876 39 // we actually forward _promoHead each time we process a promoted oop.
ysr@1876 40 // Note that this is not necessary in general (i.e. when we don't need to
ysr@1876 41 // call PromotionInfo::verify()) because oop_iterate can only add to the
ysr@1876 42 // end of _promoTail, and never needs to look at _promoHead.
ysr@1876 43
ysr@1876 44 #define PROMOTED_OOPS_ITERATE_DEFN(OopClosureType, nv_suffix) \
ysr@1876 45 \
ysr@1876 46 void PromotionInfo::promoted_oops_iterate##nv_suffix(OopClosureType* cl) { \
ysr@1876 47 NOT_PRODUCT(verify()); \
ysr@1876 48 PromotedObject *curObj, *nextObj; \
ysr@1876 49 for (curObj = _promoHead; curObj != NULL; curObj = nextObj) { \
ysr@1876 50 if ((nextObj = curObj->next()) == NULL) { \
ysr@1876 51 /* protect ourselves against additions due to closure application \
ysr@1876 52 below by resetting the list. */ \
ysr@1876 53 assert(_promoTail == curObj, "Should have been the tail"); \
ysr@1876 54 _promoHead = _promoTail = NULL; \
ysr@1876 55 } \
ysr@1876 56 if (curObj->hasDisplacedMark()) { \
ysr@1876 57 /* restore displaced header */ \
ysr@1876 58 oop(curObj)->set_mark(nextDisplacedHeader()); \
ysr@1876 59 } else { \
ysr@1876 60 /* restore prototypical header */ \
ysr@1876 61 oop(curObj)->init_mark(); \
ysr@1876 62 } \
ysr@1876 63 /* The "promoted_mark" should now not be set */ \
ysr@1876 64 assert(!curObj->hasPromotedMark(), \
ysr@1876 65 "Should have been cleared by restoring displaced mark-word"); \
ysr@1876 66 NOT_PRODUCT(_promoHead = nextObj); \
ysr@1876 67 if (cl != NULL) oop(curObj)->oop_iterate(cl); \
ysr@1876 68 if (nextObj == NULL) { /* start at head of list reset above */ \
ysr@1876 69 nextObj = _promoHead; \
ysr@1876 70 } \
ysr@1876 71 } \
ysr@1876 72 assert(noPromotions(), "post-condition violation"); \
ysr@1876 73 assert(_promoHead == NULL && _promoTail == NULL, "emptied promoted list");\
ysr@1876 74 assert(_spoolHead == _spoolTail, "emptied spooling buffers"); \
ysr@1876 75 assert(_firstIndex == _nextIndex, "empty buffer"); \
ysr@1876 76 }
ysr@1876 77
ysr@1876 78 // This should have been ALL_SINCE_...() just like the others,
ysr@1876 79 // but, because the body of the method above is somehwat longer,
ysr@1876 80 // the MSVC compiler cannot cope; as a workaround, we split the
ysr@1876 81 // macro into its 3 constituent parts below (see original macro
ysr@1876 82 // definition in specializedOopClosures.hpp).
ysr@1876 83 SPECIALIZED_SINCE_SAVE_MARKS_CLOSURES_YOUNG(PROMOTED_OOPS_ITERATE_DEFN)
ysr@1876 84 PROMOTED_OOPS_ITERATE_DEFN(OopsInGenClosure,_v)
ysr@1876 85
ysr@1876 86
ysr@1876 87 // Return the next displaced header, incrementing the pointer and
ysr@1876 88 // recycling spool area as necessary.
ysr@1876 89 markOop PromotionInfo::nextDisplacedHeader() {
ysr@1876 90 assert(_spoolHead != NULL, "promotionInfo inconsistency");
ysr@1876 91 assert(_spoolHead != _spoolTail || _firstIndex < _nextIndex,
ysr@1876 92 "Empty spool space: no displaced header can be fetched");
ysr@1876 93 assert(_spoolHead->bufferSize > _firstIndex, "Off by one error at head?");
ysr@1876 94 markOop hdr = _spoolHead->displacedHdr[_firstIndex];
ysr@1876 95 // Spool forward
ysr@1876 96 if (++_firstIndex == _spoolHead->bufferSize) { // last location in this block
ysr@1876 97 // forward to next block, recycling this block into spare spool buffer
ysr@1876 98 SpoolBlock* tmp = _spoolHead->nextSpoolBlock;
ysr@1876 99 assert(_spoolHead != _spoolTail, "Spooling storage mix-up");
ysr@1876 100 _spoolHead->nextSpoolBlock = _spareSpool;
ysr@1876 101 _spareSpool = _spoolHead;
ysr@1876 102 _spoolHead = tmp;
ysr@1876 103 _firstIndex = 1;
ysr@1876 104 NOT_PRODUCT(
ysr@1876 105 if (_spoolHead == NULL) { // all buffers fully consumed
ysr@1876 106 assert(_spoolTail == NULL && _nextIndex == 1,
ysr@1876 107 "spool buffers processing inconsistency");
ysr@1876 108 }
ysr@1876 109 )
ysr@1876 110 }
ysr@1876 111 return hdr;
ysr@1876 112 }
ysr@1876 113
ysr@1876 114 void PromotionInfo::track(PromotedObject* trackOop) {
ysr@1876 115 track(trackOop, oop(trackOop)->klass());
ysr@1876 116 }
ysr@1876 117
ysr@1876 118 void PromotionInfo::track(PromotedObject* trackOop, klassOop klassOfOop) {
ysr@1876 119 // make a copy of header as it may need to be spooled
ysr@1876 120 markOop mark = oop(trackOop)->mark();
ysr@1876 121 trackOop->clearNext();
ysr@1876 122 if (mark->must_be_preserved_for_cms_scavenge(klassOfOop)) {
ysr@1876 123 // save non-prototypical header, and mark oop
ysr@1876 124 saveDisplacedHeader(mark);
ysr@1876 125 trackOop->setDisplacedMark();
ysr@1876 126 } else {
ysr@1876 127 // we'd like to assert something like the following:
ysr@1876 128 // assert(mark == markOopDesc::prototype(), "consistency check");
ysr@1876 129 // ... but the above won't work because the age bits have not (yet) been
ysr@1876 130 // cleared. The remainder of the check would be identical to the
ysr@1876 131 // condition checked in must_be_preserved() above, so we don't really
ysr@1876 132 // have anything useful to check here!
ysr@1876 133 }
ysr@1876 134 if (_promoTail != NULL) {
ysr@1876 135 assert(_promoHead != NULL, "List consistency");
ysr@1876 136 _promoTail->setNext(trackOop);
ysr@1876 137 _promoTail = trackOop;
ysr@1876 138 } else {
ysr@1876 139 assert(_promoHead == NULL, "List consistency");
ysr@1876 140 _promoHead = _promoTail = trackOop;
ysr@1876 141 }
ysr@1876 142 // Mask as newly promoted, so we can skip over such objects
ysr@1876 143 // when scanning dirty cards
ysr@1876 144 assert(!trackOop->hasPromotedMark(), "Should not have been marked");
ysr@1876 145 trackOop->setPromotedMark();
ysr@1876 146 }
ysr@1876 147
ysr@1876 148 // Save the given displaced header, incrementing the pointer and
ysr@1876 149 // obtaining more spool area as necessary.
ysr@1876 150 void PromotionInfo::saveDisplacedHeader(markOop hdr) {
ysr@1876 151 assert(_spoolHead != NULL && _spoolTail != NULL,
ysr@1876 152 "promotionInfo inconsistency");
ysr@1876 153 assert(_spoolTail->bufferSize > _nextIndex, "Off by one error at tail?");
ysr@1876 154 _spoolTail->displacedHdr[_nextIndex] = hdr;
ysr@1876 155 // Spool forward
ysr@1876 156 if (++_nextIndex == _spoolTail->bufferSize) { // last location in this block
ysr@1876 157 // get a new spooling block
ysr@1876 158 assert(_spoolTail->nextSpoolBlock == NULL, "tail should terminate spool list");
ysr@1876 159 _splice_point = _spoolTail; // save for splicing
ysr@1876 160 _spoolTail->nextSpoolBlock = getSpoolBlock(); // might fail
ysr@1876 161 _spoolTail = _spoolTail->nextSpoolBlock; // might become NULL ...
ysr@1876 162 // ... but will attempt filling before next promotion attempt
ysr@1876 163 _nextIndex = 1;
ysr@1876 164 }
ysr@1876 165 }
ysr@1876 166
ysr@1876 167 // Ensure that spooling space exists. Return false if spooling space
ysr@1876 168 // could not be obtained.
ysr@1876 169 bool PromotionInfo::ensure_spooling_space_work() {
ysr@1876 170 assert(!has_spooling_space(), "Only call when there is no spooling space");
ysr@1876 171 // Try and obtain more spooling space
ysr@1876 172 SpoolBlock* newSpool = getSpoolBlock();
ysr@1876 173 assert(newSpool == NULL ||
ysr@1876 174 (newSpool->bufferSize != 0 && newSpool->nextSpoolBlock == NULL),
ysr@1876 175 "getSpoolBlock() sanity check");
ysr@1876 176 if (newSpool == NULL) {
ysr@1876 177 return false;
ysr@1876 178 }
ysr@1876 179 _nextIndex = 1;
ysr@1876 180 if (_spoolTail == NULL) {
ysr@1876 181 _spoolTail = newSpool;
ysr@1876 182 if (_spoolHead == NULL) {
ysr@1876 183 _spoolHead = newSpool;
ysr@1876 184 _firstIndex = 1;
ysr@1876 185 } else {
ysr@1876 186 assert(_splice_point != NULL && _splice_point->nextSpoolBlock == NULL,
ysr@1876 187 "Splice point invariant");
ysr@1876 188 // Extra check that _splice_point is connected to list
ysr@1876 189 #ifdef ASSERT
ysr@1876 190 {
ysr@1876 191 SpoolBlock* blk = _spoolHead;
ysr@1876 192 for (; blk->nextSpoolBlock != NULL;
ysr@1876 193 blk = blk->nextSpoolBlock);
ysr@1876 194 assert(blk != NULL && blk == _splice_point,
ysr@1876 195 "Splice point incorrect");
ysr@1876 196 }
ysr@1876 197 #endif // ASSERT
ysr@1876 198 _splice_point->nextSpoolBlock = newSpool;
ysr@1876 199 }
ysr@1876 200 } else {
ysr@1876 201 assert(_spoolHead != NULL, "spool list consistency");
ysr@1876 202 _spoolTail->nextSpoolBlock = newSpool;
ysr@1876 203 _spoolTail = newSpool;
ysr@1876 204 }
ysr@1876 205 return true;
ysr@1876 206 }
ysr@1876 207
ysr@1876 208 // Get a free spool buffer from the free pool, getting a new block
ysr@1876 209 // from the heap if necessary.
ysr@1876 210 SpoolBlock* PromotionInfo::getSpoolBlock() {
ysr@1876 211 SpoolBlock* res;
ysr@1876 212 if ((res = _spareSpool) != NULL) {
ysr@1876 213 _spareSpool = _spareSpool->nextSpoolBlock;
ysr@1876 214 res->nextSpoolBlock = NULL;
ysr@1876 215 } else { // spare spool exhausted, get some from heap
ysr@1876 216 res = (SpoolBlock*)(space()->allocateScratch(refillSize()));
ysr@1876 217 if (res != NULL) {
ysr@1876 218 res->init();
ysr@1876 219 }
ysr@1876 220 }
ysr@1876 221 assert(res == NULL || res->nextSpoolBlock == NULL, "postcondition");
ysr@1876 222 return res;
ysr@1876 223 }
ysr@1876 224
ysr@1876 225 void PromotionInfo::startTrackingPromotions() {
ysr@1876 226 assert(_spoolHead == _spoolTail && _firstIndex == _nextIndex,
ysr@1876 227 "spooling inconsistency?");
ysr@1876 228 _firstIndex = _nextIndex = 1;
ysr@1876 229 _tracking = true;
ysr@1876 230 }
ysr@1876 231
ysr@1876 232 #define CMSPrintPromoBlockInfo 1
ysr@1876 233
ysr@1876 234 void PromotionInfo::stopTrackingPromotions(uint worker_id) {
ysr@1876 235 assert(_spoolHead == _spoolTail && _firstIndex == _nextIndex,
ysr@1876 236 "spooling inconsistency?");
ysr@1876 237 _firstIndex = _nextIndex = 1;
ysr@1876 238 _tracking = false;
ysr@1876 239 if (CMSPrintPromoBlockInfo > 1) {
ysr@1876 240 print_statistics(worker_id);
ysr@1876 241 }
ysr@1876 242 }
ysr@1876 243
ysr@1876 244 void PromotionInfo::print_statistics(uint worker_id) const {
ysr@1876 245 assert(_spoolHead == _spoolTail && _firstIndex == _nextIndex,
ysr@1876 246 "Else will undercount");
ysr@1876 247 assert(CMSPrintPromoBlockInfo > 0, "Else unnecessary call");
ysr@1876 248 // Count the number of blocks and slots in the free pool
ysr@1876 249 size_t slots = 0;
ysr@1876 250 size_t blocks = 0;
ysr@1876 251 for (SpoolBlock* cur_spool = _spareSpool;
ysr@1876 252 cur_spool != NULL;
ysr@1876 253 cur_spool = cur_spool->nextSpoolBlock) {
ysr@1876 254 // the first entry is just a self-pointer; indices 1 through
ysr@1876 255 // bufferSize - 1 are occupied (thus, bufferSize - 1 slots).
ysr@1876 256 guarantee((void*)cur_spool->displacedHdr == (void*)&cur_spool->displacedHdr,
ysr@1876 257 "first entry of displacedHdr should be self-referential");
ysr@1876 258 slots += cur_spool->bufferSize - 1;
ysr@1876 259 blocks++;
ysr@1876 260 }
ysr@1876 261 if (_spoolHead != NULL) {
ysr@1876 262 slots += _spoolHead->bufferSize - 1;
ysr@1876 263 blocks++;
ysr@1876 264 }
ysr@1876 265 gclog_or_tty->print_cr(" [worker %d] promo_blocks = %d, promo_slots = %d ",
ysr@1876 266 worker_id, blocks, slots);
ysr@1876 267 }
ysr@1876 268
ysr@1876 269 // When _spoolTail is not NULL, then the slot <_spoolTail, _nextIndex>
ysr@1876 270 // points to the next slot available for filling.
ysr@1876 271 // The set of slots holding displaced headers are then all those in the
ysr@1876 272 // right-open interval denoted by:
ysr@1876 273 //
ysr@1876 274 // [ <_spoolHead, _firstIndex>, <_spoolTail, _nextIndex> )
ysr@1876 275 //
ysr@1876 276 // When _spoolTail is NULL, then the set of slots with displaced headers
ysr@1876 277 // is all those starting at the slot <_spoolHead, _firstIndex> and
ysr@1876 278 // going up to the last slot of last block in the linked list.
ysr@1876 279 // In this lartter case, _splice_point points to the tail block of
ysr@1876 280 // this linked list of blocks holding displaced headers.
ysr@1876 281 void PromotionInfo::verify() const {
ysr@1876 282 // Verify the following:
ysr@1876 283 // 1. the number of displaced headers matches the number of promoted
ysr@1876 284 // objects that have displaced headers
ysr@1876 285 // 2. each promoted object lies in this space
ysr@1876 286 debug_only(
ysr@1876 287 PromotedObject* junk = NULL;
ysr@1876 288 assert(junk->next_addr() == (void*)(oop(junk)->mark_addr()),
ysr@1876 289 "Offset of PromotedObject::_next is expected to align with "
ysr@1876 290 " the OopDesc::_mark within OopDesc");
ysr@1876 291 )
ysr@1876 292 // FIXME: guarantee????
ysr@1876 293 guarantee(_spoolHead == NULL || _spoolTail != NULL ||
ysr@1876 294 _splice_point != NULL, "list consistency");
ysr@1876 295 guarantee(_promoHead == NULL || _promoTail != NULL, "list consistency");
ysr@1876 296 // count the number of objects with displaced headers
ysr@1876 297 size_t numObjsWithDisplacedHdrs = 0;
ysr@1876 298 for (PromotedObject* curObj = _promoHead; curObj != NULL; curObj = curObj->next()) {
ysr@1876 299 guarantee(space()->is_in_reserved((HeapWord*)curObj), "Containment");
ysr@1876 300 // the last promoted object may fail the mark() != NULL test of is_oop().
ysr@1876 301 guarantee(curObj->next() == NULL || oop(curObj)->is_oop(), "must be an oop");
ysr@1876 302 if (curObj->hasDisplacedMark()) {
ysr@1876 303 numObjsWithDisplacedHdrs++;
ysr@1876 304 }
ysr@1876 305 }
ysr@1876 306 // Count the number of displaced headers
ysr@1876 307 size_t numDisplacedHdrs = 0;
ysr@1876 308 for (SpoolBlock* curSpool = _spoolHead;
ysr@1876 309 curSpool != _spoolTail && curSpool != NULL;
ysr@1876 310 curSpool = curSpool->nextSpoolBlock) {
ysr@1876 311 // the first entry is just a self-pointer; indices 1 through
ysr@1876 312 // bufferSize - 1 are occupied (thus, bufferSize - 1 slots).
ysr@1876 313 guarantee((void*)curSpool->displacedHdr == (void*)&curSpool->displacedHdr,
ysr@1876 314 "first entry of displacedHdr should be self-referential");
ysr@1876 315 numDisplacedHdrs += curSpool->bufferSize - 1;
ysr@1876 316 }
ysr@1876 317 guarantee((_spoolHead == _spoolTail) == (numDisplacedHdrs == 0),
ysr@1876 318 "internal consistency");
ysr@1876 319 guarantee(_spoolTail != NULL || _nextIndex == 1,
ysr@1876 320 "Inconsistency between _spoolTail and _nextIndex");
ysr@1876 321 // We overcounted (_firstIndex-1) worth of slots in block
ysr@1876 322 // _spoolHead and we undercounted (_nextIndex-1) worth of
ysr@1876 323 // slots in block _spoolTail. We make an appropriate
ysr@1876 324 // adjustment by subtracting the first and adding the
ysr@1876 325 // second: - (_firstIndex - 1) + (_nextIndex - 1)
ysr@1876 326 numDisplacedHdrs += (_nextIndex - _firstIndex);
ysr@1876 327 guarantee(numDisplacedHdrs == numObjsWithDisplacedHdrs, "Displaced hdr count");
ysr@1876 328 }
ysr@1876 329
ysr@1876 330 void PromotionInfo::print_on(outputStream* st) const {
ysr@1876 331 SpoolBlock* curSpool = NULL;
ysr@1876 332 size_t i = 0;
ysr@2071 333 st->print_cr(" start & end indices: [" SIZE_FORMAT ", " SIZE_FORMAT ")",
ysr@1876 334 _firstIndex, _nextIndex);
ysr@1876 335 for (curSpool = _spoolHead; curSpool != _spoolTail && curSpool != NULL;
ysr@1876 336 curSpool = curSpool->nextSpoolBlock) {
ysr@1876 337 curSpool->print_on(st);
ysr@1876 338 st->print_cr(" active ");
ysr@1876 339 i++;
ysr@1876 340 }
ysr@1876 341 for (curSpool = _spoolTail; curSpool != NULL;
ysr@1876 342 curSpool = curSpool->nextSpoolBlock) {
ysr@1876 343 curSpool->print_on(st);
ysr@1876 344 st->print_cr(" inactive ");
ysr@1876 345 i++;
ysr@1876 346 }
ysr@1876 347 for (curSpool = _spareSpool; curSpool != NULL;
ysr@1876 348 curSpool = curSpool->nextSpoolBlock) {
ysr@1876 349 curSpool->print_on(st);
ysr@1876 350 st->print_cr(" free ");
ysr@1876 351 i++;
ysr@1876 352 }
ysr@2071 353 st->print_cr(" " SIZE_FORMAT " header spooling blocks", i);
ysr@1876 354 }
ysr@1876 355
ysr@1876 356 void SpoolBlock::print_on(outputStream* st) const {
ysr@1876 357 st->print("[" PTR_FORMAT "," PTR_FORMAT "), " SIZE_FORMAT " HeapWords -> " PTR_FORMAT,
ysr@1876 358 this, (HeapWord*)displacedHdr + bufferSize,
ysr@1876 359 bufferSize, nextSpoolBlock);
ysr@1876 360 }

mercurial