| 1 | /* | 
|---|
| 2 | * Copyright (c) 2010, 2018, 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 | */ | 
|---|
| 24 |  | 
|---|
| 25 | #include "precompiled.hpp" | 
|---|
| 26 | #include "gc/cms/compactibleFreeListSpace.hpp" | 
|---|
| 27 | #include "gc/cms/promotionInfo.hpp" | 
|---|
| 28 | #include "gc/shared/genOopClosures.hpp" | 
|---|
| 29 | #include "oops/compressedOops.inline.hpp" | 
|---|
| 30 | #include "oops/markOop.inline.hpp" | 
|---|
| 31 | #include "oops/oop.hpp" | 
|---|
| 32 |  | 
|---|
| 33 | ///////////////////////////////////////////////////////////////////////// | 
|---|
| 34 | //// PromotionInfo | 
|---|
| 35 | ///////////////////////////////////////////////////////////////////////// | 
|---|
| 36 |  | 
|---|
| 37 |  | 
|---|
| 38 | PromotedObject* PromotedObject::next() const { | 
|---|
| 39 | assert(!((FreeChunk*)this)->is_free(), "Error"); | 
|---|
| 40 | PromotedObject* res; | 
|---|
| 41 | if (UseCompressedOops) { | 
|---|
| 42 | // The next pointer is a compressed oop stored in the top 32 bits | 
|---|
| 43 | res = (PromotedObject*)CompressedOops::decode(_data._narrow_next); | 
|---|
| 44 | } else { | 
|---|
| 45 | res = (PromotedObject*)(_next & next_mask); | 
|---|
| 46 | } | 
|---|
| 47 | assert(oopDesc::is_oop_or_null(oop(res), true /* ignore mark word */), "Expected an oop or NULL at "PTR_FORMAT, p2i(oop(res))); | 
|---|
| 48 | return res; | 
|---|
| 49 | } | 
|---|
| 50 |  | 
|---|
| 51 | inline void PromotedObject::setNext(PromotedObject* x) { | 
|---|
| 52 | assert(((intptr_t)x & ~next_mask) == 0, "Conflict in bit usage, " | 
|---|
| 53 | "or insufficient alignment of objects"); | 
|---|
| 54 | if (UseCompressedOops) { | 
|---|
| 55 | assert(_data._narrow_next == 0, "Overwrite?"); | 
|---|
| 56 | _data._narrow_next = CompressedOops::encode(oop(x)); | 
|---|
| 57 | } else { | 
|---|
| 58 | _next |= (intptr_t)x; | 
|---|
| 59 | } | 
|---|
| 60 | assert(!((FreeChunk*)this)->is_free(), "Error"); | 
|---|
| 61 | } | 
|---|
| 62 |  | 
|---|
| 63 | // Return the next displaced header, incrementing the pointer and | 
|---|
| 64 | // recycling spool area as necessary. | 
|---|
| 65 | markOop PromotionInfo::() { | 
|---|
| 66 | assert(_spoolHead != NULL, "promotionInfo inconsistency"); | 
|---|
| 67 | assert(_spoolHead != _spoolTail || _firstIndex < _nextIndex, | 
|---|
| 68 | "Empty spool space: no displaced header can be fetched"); | 
|---|
| 69 | assert(_spoolHead->bufferSize > _firstIndex, "Off by one error at head?"); | 
|---|
| 70 | markOop hdr = _spoolHead->displacedHdr[_firstIndex]; | 
|---|
| 71 | // Spool forward | 
|---|
| 72 | if (++_firstIndex == _spoolHead->bufferSize) { // last location in this block | 
|---|
| 73 | // forward to next block, recycling this block into spare spool buffer | 
|---|
| 74 | SpoolBlock* tmp = _spoolHead->nextSpoolBlock; | 
|---|
| 75 | assert(_spoolHead != _spoolTail, "Spooling storage mix-up"); | 
|---|
| 76 | _spoolHead->nextSpoolBlock = _spareSpool; | 
|---|
| 77 | _spareSpool = _spoolHead; | 
|---|
| 78 | _spoolHead = tmp; | 
|---|
| 79 | _firstIndex = 1; | 
|---|
| 80 | NOT_PRODUCT( | 
|---|
| 81 | if (_spoolHead == NULL) {  // all buffers fully consumed | 
|---|
| 82 | assert(_spoolTail == NULL && _nextIndex == 1, | 
|---|
| 83 | "spool buffers processing inconsistency"); | 
|---|
| 84 | } | 
|---|
| 85 | ) | 
|---|
| 86 | } | 
|---|
| 87 | return hdr; | 
|---|
| 88 | } | 
|---|
| 89 |  | 
|---|
| 90 | void PromotionInfo::track(PromotedObject* trackOop) { | 
|---|
| 91 | track(trackOop, oop(trackOop)->klass()); | 
|---|
| 92 | } | 
|---|
| 93 |  | 
|---|
| 94 | void PromotionInfo::track(PromotedObject* trackOop, Klass* klassOfOop) { | 
|---|
| 95 | // make a copy of header as it may need to be spooled | 
|---|
| 96 | markOop mark = oop(trackOop)->mark_raw(); | 
|---|
| 97 | trackOop->clear_next(); | 
|---|
| 98 | if (mark->must_be_preserved_for_cms_scavenge(klassOfOop)) { | 
|---|
| 99 | // save non-prototypical header, and mark oop | 
|---|
| 100 | saveDisplacedHeader(mark); | 
|---|
| 101 | trackOop->setDisplacedMark(); | 
|---|
| 102 | } else { | 
|---|
| 103 | // we'd like to assert something like the following: | 
|---|
| 104 | // assert(mark == markOopDesc::prototype(), "consistency check"); | 
|---|
| 105 | // ... but the above won't work because the age bits have not (yet) been | 
|---|
| 106 | // cleared. The remainder of the check would be identical to the | 
|---|
| 107 | // condition checked in must_be_preserved() above, so we don't really | 
|---|
| 108 | // have anything useful to check here! | 
|---|
| 109 | } | 
|---|
| 110 | if (_promoTail != NULL) { | 
|---|
| 111 | assert(_promoHead != NULL, "List consistency"); | 
|---|
| 112 | _promoTail->setNext(trackOop); | 
|---|
| 113 | _promoTail = trackOop; | 
|---|
| 114 | } else { | 
|---|
| 115 | assert(_promoHead == NULL, "List consistency"); | 
|---|
| 116 | _promoHead = _promoTail = trackOop; | 
|---|
| 117 | } | 
|---|
| 118 | // Mask as newly promoted, so we can skip over such objects | 
|---|
| 119 | // when scanning dirty cards | 
|---|
| 120 | assert(!trackOop->hasPromotedMark(), "Should not have been marked"); | 
|---|
| 121 | trackOop->setPromotedMark(); | 
|---|
| 122 | } | 
|---|
| 123 |  | 
|---|
| 124 | // Save the given displaced header, incrementing the pointer and | 
|---|
| 125 | // obtaining more spool area as necessary. | 
|---|
| 126 | void PromotionInfo::(markOop hdr) { | 
|---|
| 127 | assert(_spoolHead != NULL && _spoolTail != NULL, | 
|---|
| 128 | "promotionInfo inconsistency"); | 
|---|
| 129 | assert(_spoolTail->bufferSize > _nextIndex, "Off by one error at tail?"); | 
|---|
| 130 | _spoolTail->displacedHdr[_nextIndex] = hdr; | 
|---|
| 131 | // Spool forward | 
|---|
| 132 | if (++_nextIndex == _spoolTail->bufferSize) { // last location in this block | 
|---|
| 133 | // get a new spooling block | 
|---|
| 134 | assert(_spoolTail->nextSpoolBlock == NULL, "tail should terminate spool list"); | 
|---|
| 135 | _splice_point = _spoolTail;                   // save for splicing | 
|---|
| 136 | _spoolTail->nextSpoolBlock = getSpoolBlock(); // might fail | 
|---|
| 137 | _spoolTail = _spoolTail->nextSpoolBlock;      // might become NULL ... | 
|---|
| 138 | // ... but will attempt filling before next promotion attempt | 
|---|
| 139 | _nextIndex = 1; | 
|---|
| 140 | } | 
|---|
| 141 | } | 
|---|
| 142 |  | 
|---|
| 143 | // Ensure that spooling space exists. Return false if spooling space | 
|---|
| 144 | // could not be obtained. | 
|---|
| 145 | bool PromotionInfo::ensure_spooling_space_work() { | 
|---|
| 146 | assert(!has_spooling_space(), "Only call when there is no spooling space"); | 
|---|
| 147 | // Try and obtain more spooling space | 
|---|
| 148 | SpoolBlock* newSpool = getSpoolBlock(); | 
|---|
| 149 | assert(newSpool == NULL || | 
|---|
| 150 | (newSpool->bufferSize != 0 && newSpool->nextSpoolBlock == NULL), | 
|---|
| 151 | "getSpoolBlock() sanity check"); | 
|---|
| 152 | if (newSpool == NULL) { | 
|---|
| 153 | return false; | 
|---|
| 154 | } | 
|---|
| 155 | _nextIndex = 1; | 
|---|
| 156 | if (_spoolTail == NULL) { | 
|---|
| 157 | _spoolTail = newSpool; | 
|---|
| 158 | if (_spoolHead == NULL) { | 
|---|
| 159 | _spoolHead = newSpool; | 
|---|
| 160 | _firstIndex = 1; | 
|---|
| 161 | } else { | 
|---|
| 162 | assert(_splice_point != NULL && _splice_point->nextSpoolBlock == NULL, | 
|---|
| 163 | "Splice point invariant"); | 
|---|
| 164 | // Extra check that _splice_point is connected to list | 
|---|
| 165 | #ifdef ASSERT | 
|---|
| 166 | { | 
|---|
| 167 | SpoolBlock* blk = _spoolHead; | 
|---|
| 168 | for (; blk->nextSpoolBlock != NULL; | 
|---|
| 169 | blk = blk->nextSpoolBlock); | 
|---|
| 170 | assert(blk != NULL && blk == _splice_point, | 
|---|
| 171 | "Splice point incorrect"); | 
|---|
| 172 | } | 
|---|
| 173 | #endif // ASSERT | 
|---|
| 174 | _splice_point->nextSpoolBlock = newSpool; | 
|---|
| 175 | } | 
|---|
| 176 | } else { | 
|---|
| 177 | assert(_spoolHead != NULL, "spool list consistency"); | 
|---|
| 178 | _spoolTail->nextSpoolBlock = newSpool; | 
|---|
| 179 | _spoolTail = newSpool; | 
|---|
| 180 | } | 
|---|
| 181 | return true; | 
|---|
| 182 | } | 
|---|
| 183 |  | 
|---|
| 184 | // Get a free spool buffer from the free pool, getting a new block | 
|---|
| 185 | // from the heap if necessary. | 
|---|
| 186 | SpoolBlock* PromotionInfo::getSpoolBlock() { | 
|---|
| 187 | SpoolBlock* res; | 
|---|
| 188 | if ((res = _spareSpool) != NULL) { | 
|---|
| 189 | _spareSpool = _spareSpool->nextSpoolBlock; | 
|---|
| 190 | res->nextSpoolBlock = NULL; | 
|---|
| 191 | } else {  // spare spool exhausted, get some from heap | 
|---|
| 192 | res = (SpoolBlock*)(space()->allocateScratch(refillSize())); | 
|---|
| 193 | if (res != NULL) { | 
|---|
| 194 | res->init(); | 
|---|
| 195 | } | 
|---|
| 196 | } | 
|---|
| 197 | assert(res == NULL || res->nextSpoolBlock == NULL, "postcondition"); | 
|---|
| 198 | return res; | 
|---|
| 199 | } | 
|---|
| 200 |  | 
|---|
| 201 | void PromotionInfo::startTrackingPromotions() { | 
|---|
| 202 | assert(noPromotions(), "sanity"); | 
|---|
| 203 | assert(_spoolHead == _spoolTail && _firstIndex == _nextIndex, | 
|---|
| 204 | "spooling inconsistency?"); | 
|---|
| 205 | _firstIndex = _nextIndex = 1; | 
|---|
| 206 | _tracking = true; | 
|---|
| 207 | } | 
|---|
| 208 |  | 
|---|
| 209 | void PromotionInfo::stopTrackingPromotions() { | 
|---|
| 210 | assert(noPromotions(), "we should have torn down the lists by now"); | 
|---|
| 211 | assert(_spoolHead == _spoolTail && _firstIndex == _nextIndex, | 
|---|
| 212 | "spooling inconsistency?"); | 
|---|
| 213 | _firstIndex = _nextIndex = 1; | 
|---|
| 214 | _tracking = false; | 
|---|
| 215 | } | 
|---|
| 216 |  | 
|---|
| 217 | // When _spoolTail is not NULL, then the slot <_spoolTail, _nextIndex> | 
|---|
| 218 | // points to the next slot available for filling. | 
|---|
| 219 | // The set of slots holding displaced headers are then all those in the | 
|---|
| 220 | // right-open interval denoted by: | 
|---|
| 221 | // | 
|---|
| 222 | //    [ <_spoolHead, _firstIndex>, <_spoolTail, _nextIndex> ) | 
|---|
| 223 | // | 
|---|
| 224 | // When _spoolTail is NULL, then the set of slots with displaced headers | 
|---|
| 225 | // is all those starting at the slot <_spoolHead, _firstIndex> and | 
|---|
| 226 | // going up to the last slot of last block in the linked list. | 
|---|
| 227 | // In this latter case, _splice_point points to the tail block of | 
|---|
| 228 | // this linked list of blocks holding displaced headers. | 
|---|
| 229 | void PromotionInfo::verify() const { | 
|---|
| 230 | // Verify the following: | 
|---|
| 231 | // 1. the number of displaced headers matches the number of promoted | 
|---|
| 232 | //    objects that have displaced headers | 
|---|
| 233 | // 2. each promoted object lies in this space | 
|---|
| 234 | debug_only( | 
|---|
| 235 | PromotedObject* junk = NULL; | 
|---|
| 236 | assert(junk->next_addr() == (void*)(oop(junk)->mark_addr_raw()), | 
|---|
| 237 | "Offset of PromotedObject::_next is expected to align with " | 
|---|
| 238 | "  the OopDesc::_mark within OopDesc"); | 
|---|
| 239 | ) | 
|---|
| 240 | // FIXME: guarantee???? | 
|---|
| 241 | guarantee(_spoolHead == NULL || _spoolTail != NULL || | 
|---|
| 242 | _splice_point != NULL, "list consistency"); | 
|---|
| 243 | guarantee(_promoHead == NULL || _promoTail != NULL, "list consistency"); | 
|---|
| 244 | // count the number of objects with displaced headers | 
|---|
| 245 | size_t numObjsWithDisplacedHdrs = 0; | 
|---|
| 246 | for (PromotedObject* curObj = _promoHead; curObj != NULL; curObj = curObj->next()) { | 
|---|
| 247 | guarantee(space()->is_in_reserved((HeapWord*)curObj), "Containment"); | 
|---|
| 248 | // the last promoted object may fail the mark() != NULL test of is_oop(). | 
|---|
| 249 | guarantee(curObj->next() == NULL || oopDesc::is_oop(oop(curObj)), "must be an oop"); | 
|---|
| 250 | if (curObj->hasDisplacedMark()) { | 
|---|
| 251 | numObjsWithDisplacedHdrs++; | 
|---|
| 252 | } | 
|---|
| 253 | } | 
|---|
| 254 | // Count the number of displaced headers | 
|---|
| 255 | size_t numDisplacedHdrs = 0; | 
|---|
| 256 | for (SpoolBlock* curSpool = _spoolHead; | 
|---|
| 257 | curSpool != _spoolTail && curSpool != NULL; | 
|---|
| 258 | curSpool = curSpool->nextSpoolBlock) { | 
|---|
| 259 | // the first entry is just a self-pointer; indices 1 through | 
|---|
| 260 | // bufferSize - 1 are occupied (thus, bufferSize - 1 slots). | 
|---|
| 261 | guarantee((void*)curSpool->displacedHdr == (void*)&curSpool->displacedHdr, | 
|---|
| 262 | "first entry of displacedHdr should be self-referential"); | 
|---|
| 263 | numDisplacedHdrs += curSpool->bufferSize - 1; | 
|---|
| 264 | } | 
|---|
| 265 | guarantee((_spoolHead == _spoolTail) == (numDisplacedHdrs == 0), | 
|---|
| 266 | "internal consistency"); | 
|---|
| 267 | guarantee(_spoolTail != NULL || _nextIndex == 1, | 
|---|
| 268 | "Inconsistency between _spoolTail and _nextIndex"); | 
|---|
| 269 | // We overcounted (_firstIndex-1) worth of slots in block | 
|---|
| 270 | // _spoolHead and we undercounted (_nextIndex-1) worth of | 
|---|
| 271 | // slots in block _spoolTail. We make an appropriate | 
|---|
| 272 | // adjustment by subtracting the first and adding the | 
|---|
| 273 | // second:  - (_firstIndex - 1) + (_nextIndex - 1) | 
|---|
| 274 | numDisplacedHdrs += (_nextIndex - _firstIndex); | 
|---|
| 275 | guarantee(numDisplacedHdrs == numObjsWithDisplacedHdrs, "Displaced hdr count"); | 
|---|
| 276 | } | 
|---|
| 277 |  | 
|---|
| 278 | void PromotionInfo::print_on(outputStream* st) const { | 
|---|
| 279 | SpoolBlock* curSpool = NULL; | 
|---|
| 280 | size_t i = 0; | 
|---|
| 281 | st->print_cr( " start & end indices: ["SIZE_FORMAT ", "SIZE_FORMAT ")", | 
|---|
| 282 | _firstIndex, _nextIndex); | 
|---|
| 283 | for (curSpool = _spoolHead; curSpool != _spoolTail && curSpool != NULL; | 
|---|
| 284 | curSpool = curSpool->nextSpoolBlock) { | 
|---|
| 285 | curSpool->print_on(st); | 
|---|
| 286 | st->print_cr( " active "); | 
|---|
| 287 | i++; | 
|---|
| 288 | } | 
|---|
| 289 | for (curSpool = _spoolTail; curSpool != NULL; | 
|---|
| 290 | curSpool = curSpool->nextSpoolBlock) { | 
|---|
| 291 | curSpool->print_on(st); | 
|---|
| 292 | st->print_cr( " inactive "); | 
|---|
| 293 | i++; | 
|---|
| 294 | } | 
|---|
| 295 | for (curSpool = _spareSpool; curSpool != NULL; | 
|---|
| 296 | curSpool = curSpool->nextSpoolBlock) { | 
|---|
| 297 | curSpool->print_on(st); | 
|---|
| 298 | st->print_cr( " free "); | 
|---|
| 299 | i++; | 
|---|
| 300 | } | 
|---|
| 301 | st->print_cr( "  "SIZE_FORMAT " header spooling blocks", i); | 
|---|
| 302 | } | 
|---|
| 303 |  | 
|---|
| 304 | void SpoolBlock::print_on(outputStream* st) const { | 
|---|
| 305 | st->print( "["PTR_FORMAT ","PTR_FORMAT "), "SIZE_FORMAT " HeapWords -> "PTR_FORMAT, | 
|---|
| 306 | p2i(this), p2i((HeapWord*)displacedHdr + bufferSize), | 
|---|
| 307 | bufferSize, p2i(nextSpoolBlock)); | 
|---|
| 308 | } | 
|---|
| 309 |  | 
|---|