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

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

mercurial