Mercurial > hg > graal-compiler
annotate src/share/vm/gc_implementation/concurrentMarkSweep/promotionInfo.cpp @ 1716:be3f9c242c9d
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
author | ysr |
---|---|
date | Mon, 16 Aug 2010 15:58:42 -0700 |
parents | c18cbe5936b8 |
children | 179464550c7d |
rev | line source |
---|---|
1521 | 1 /* |
1552
c18cbe5936b8
6941466: Oracle rebranding changes for Hotspot repositories
trims
parents:
1546
diff
changeset
|
2 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. |
1521 | 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 * | |
1552
c18cbe5936b8
6941466: Oracle rebranding changes for Hotspot repositories
trims
parents:
1546
diff
changeset
|
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
c18cbe5936b8
6941466: Oracle rebranding changes for Hotspot repositories
trims
parents:
1546
diff
changeset
|
20 * or visit www.oracle.com if you need additional information or have any |
c18cbe5936b8
6941466: Oracle rebranding changes for Hotspot repositories
trims
parents:
1546
diff
changeset
|
21 * questions. |
1521 | 22 * |
23 */ | |
24 | |
25 # include "incls/_precompiled.incl" | |
26 # include "incls/_promotionInfo.cpp.incl" | |
27 | |
28 ///////////////////////////////////////////////////////////////////////// | |
29 //// PromotionInfo | |
30 ///////////////////////////////////////////////////////////////////////// | |
31 | |
32 | |
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. | |
43 | |
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 } | |
77 | |
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) | |
85 | |
86 | |
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 } | |
113 | |
114 void PromotionInfo::track(PromotedObject* trackOop) { | |
115 track(trackOop, oop(trackOop)->klass()); | |
116 } | |
117 | |
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 } | |
147 | |
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 } | |
166 | |
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 } | |
207 | |
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 } | |
224 | |
225 void PromotionInfo::startTrackingPromotions() { | |
226 assert(_spoolHead == _spoolTail && _firstIndex == _nextIndex, | |
227 "spooling inconsistency?"); | |
228 _firstIndex = _nextIndex = 1; | |
229 _tracking = true; | |
230 } | |
231 | |
232 #define CMSPrintPromoBlockInfo 1 | |
233 | |
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 } | |
243 | |
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 } | |
268 | |
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 } | |
329 | |
330 void PromotionInfo::print_on(outputStream* st) const { | |
331 SpoolBlock* curSpool = NULL; | |
332 size_t i = 0; | |
1716
be3f9c242c9d
6948538: CMS: BOT walkers can fall into object allocation and initialization cracks
ysr
parents:
1552
diff
changeset
|
333 st->print_cr(" start & end indices: [" SIZE_FORMAT ", " SIZE_FORMAT ")", |
1521 | 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 } | |
1716
be3f9c242c9d
6948538: CMS: BOT walkers can fall into object allocation and initialization cracks
ysr
parents:
1552
diff
changeset
|
353 st->print_cr(" " SIZE_FORMAT " header spooling blocks", i); |
1521 | 354 } |
355 | |
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 } |