001 /* 002 * This file is part of the Jikes RVM project (http://jikesrvm.org). 003 * 004 * This file is licensed to You under the Eclipse Public License (EPL); 005 * You may not use this file except in compliance with the License. You 006 * may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/eclipse-1.0.php 009 * 010 * See the COPYRIGHT.txt file distributed with this work for information 011 * regarding copyright ownership. 012 */ 013 package org.mmtk.policy.immix; 014 015 import static org.mmtk.policy.immix.ImmixConstants.*; 016 017 import org.mmtk.plan.Plan; 018 import org.mmtk.plan.TransitiveClosure; 019 import org.mmtk.policy.Space; 020 import org.mmtk.utility.heap.*; 021 import org.mmtk.utility.options.LineReuseRatio; 022 import org.mmtk.utility.options.Options; 023 import org.mmtk.utility.Constants; 024 import org.mmtk.utility.ForwardingWord; 025 import org.mmtk.utility.HeaderByte; 026 import org.mmtk.utility.Log; 027 028 import org.mmtk.vm.Lock; 029 import org.mmtk.vm.VM; 030 031 import org.vmmagic.pragma.*; 032 import org.vmmagic.unboxed.*; 033 034 /** 035 * Each instance of this class corresponds to one immix <b>space</b>. 036 * Each of the instance methods of this class may be called by any 037 * thread (i.e. synchronization must be explicit in any instance or 038 * class method). This contrasts with the SquishLocal, where 039 * instances correspond to *plan* instances and therefore to kernel 040 * threads. Thus unlike this class, synchronization is not necessary 041 * in the instance methods of SquishLocal. 042 * 043 */ 044 @Uninterruptible 045 public final class ImmixSpace extends Space implements Constants { 046 047 /**************************************************************************** 048 * 049 * Class variables 050 */ 051 052 /** 053 * 054 */ 055 private static short reusableMarkStateThreshold = 0; 056 057 /**************************************************************************** 058 * 059 * Instance variables 060 */ 061 062 /** 063 * 064 */ 065 private byte markState = ObjectHeader.MARK_BASE_VALUE; 066 byte lineMarkState = RESET_LINE_MARK_STATE; 067 private byte lineUnavailState = RESET_LINE_MARK_STATE; 068 private boolean inCollection; 069 private int linesConsumed = 0; 070 071 private Lock mutatorLock = VM.newLock(getName()+"mutator"); 072 private Lock gcLock = VM.newLock(getName()+"gc"); 073 074 private Address allocBlockCursor = Address.zero(); 075 private Address allocBlockSentinel = Address.zero(); 076 private boolean exhaustedReusableSpace = true; 077 078 private final ChunkList chunkMap = new ChunkList(); 079 private final Defrag defrag; 080 081 /**************************************************************************** 082 * 083 * Initialization 084 */ 085 086 static { 087 Options.lineReuseRatio = new LineReuseRatio(); 088 reusableMarkStateThreshold = (short) (Options.lineReuseRatio.getValue() * MAX_BLOCK_MARK_STATE); 089 } 090 091 /** 092 * The caller specifies the region of virtual memory to be used for 093 * this space. If this region conflicts with an existing space, 094 * then the constructor will fail. 095 * 096 * @param name The name of this space (used when printing error messages etc) 097 * @param vmRequest The virtual memory request 098 */ 099 public ImmixSpace(String name, VMRequest vmRequest) { 100 this(name, true, vmRequest); 101 } 102 103 /** 104 * The caller specifies the region of virtual memory to be used for 105 * this space. If this region conflicts with an existing space, 106 * then the constructor will fail. 107 * 108 * @param name The name of this space (used when printing error messages etc) 109 * @param zeroed if true, allocations return zeroed memory 110 * @param vmRequest The virtual memory request 111 */ 112 public ImmixSpace(String name, boolean zeroed, VMRequest vmRequest) { 113 super(name, false, false, zeroed, vmRequest); 114 if (vmRequest.isDiscontiguous()) 115 pr = new FreeListPageResource(this, Chunk.getRequiredMetaDataPages()); 116 else 117 pr = new FreeListPageResource(this, start, extent, Chunk.getRequiredMetaDataPages()); 118 defrag = new Defrag((FreeListPageResource) pr); 119 } 120 121 /**************************************************************************** 122 * 123 * Global prepare and release 124 */ 125 126 /** 127 * Prepare for a new collection increment. 128 */ 129 public void prepare(boolean majorGC) { 130 if (majorGC) { 131 markState = ObjectHeader.deltaMarkState(markState, true); 132 lineMarkState++; 133 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(lineMarkState <= MAX_LINE_MARK_STATE); 134 } 135 chunkMap.reset(); 136 defrag.prepare(chunkMap, this); 137 inCollection = true; 138 139 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(VM.activePlan.collectorCount() <= MAX_COLLECTORS); 140 } 141 142 /** 143 * A new collection increment has completed. Release global resources. 144 * @param majorGC TODO 145 */ 146 public boolean release(boolean majorGC) { 147 boolean didDefrag = defrag.inDefrag(); 148 if (majorGC) { 149 if (lineMarkState == MAX_LINE_MARK_STATE) 150 lineMarkState = RESET_LINE_MARK_STATE; 151 lineUnavailState = lineMarkState; 152 } 153 chunkMap.reset(); 154 defrag.globalRelease(); 155 inCollection = false; 156 157 /* set up reusable space */ 158 if (allocBlockCursor.isZero()) allocBlockCursor = chunkMap.getHeadChunk(); 159 allocBlockSentinel = allocBlockCursor; 160 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(isRecycleAllocChunkAligned(allocBlockSentinel)); 161 exhaustedReusableSpace = allocBlockCursor.isZero(); 162 if (VM.VERIFY_ASSERTIONS && Options.verbose.getValue() >= 9) { 163 Log.write("gr[allocBlockCursor: "); Log.write(allocBlockCursor); Log.write(" allocBlockSentinel: "); Log.write(allocBlockSentinel); Log.writeln("]"); 164 } 165 166 /* really just want this to happen once after options are booted, but no harm in re-doing it */ 167 reusableMarkStateThreshold = (short) (Options.lineReuseRatio.getValue() * MAX_BLOCK_MARK_STATE); 168 Defrag.defragReusableMarkStateThreshold = (short) (Options.defragLineReuseRatio.getValue() * MAX_BLOCK_MARK_STATE); 169 170 linesConsumed = 0; 171 return didDefrag; 172 } 173 174 /** 175 * Determine the collection kind. 176 * 177 * @param emergencyCollection Is this collection an emergency (last did not yield enough)? 178 * @param collectWholeHeap Is this a whole heap collection? 179 * @param collectionAttempt Which attempt is this to collect? 180 * @param userTriggeredCollection Was this collection requested by the user? 181 */ 182 public void decideWhetherToDefrag(boolean emergencyCollection, boolean collectWholeHeap, int collectionAttempt, boolean userTriggeredCollection) { 183 defrag.decideWhetherToDefrag(emergencyCollection, collectWholeHeap, collectionAttempt, userTriggeredCollection, exhaustedReusableSpace); 184 } 185 186 /** 187 * Return the amount of headroom required to allow defrag, so this can be included in a collection reserve. 188 * 189 * @return The number of pages. 190 */ 191 public int defragHeadroomPages() { 192 return defrag.getDefragHeadroomPages(); 193 } 194 195 /**************************************************************************** 196 * 197 * Collection state access methods 198 */ 199 200 /** 201 * Return {@code true} if this space is currently being collected. 202 * 203 * @return {@code true} if this space is currently being collected. 204 */ 205 @Inline 206 public boolean inImmixCollection() { 207 return inCollection; 208 } 209 210 /** 211 * Return {@code true} if this space is currently being defraged. 212 * 213 * @return {@code true} if this space is currently being defraged. 214 */ 215 @Inline 216 public boolean inImmixDefragCollection() { 217 return inCollection && defrag.inDefrag(); 218 } 219 220 /** 221 * Return the number of pages allocated since the last collection 222 * 223 * @return The number of pages allocated since the last collection 224 */ 225 public int getPagesAllocated() { 226 return linesConsumed>>(LOG_BYTES_IN_PAGE-LOG_BYTES_IN_LINE); 227 } 228 229 /** 230 * Return the reusable mark state threshold, which determines how 231 * eagerly lines should be recycled (by default these values are 232 * set so that all lines are recycled). 233 * 234 * @param forDefrag The query is the context of a defragmenting collection 235 * @return The reusable mark state threshold 236 */ 237 @Inline 238 public static short getReusuableMarkStateThreshold(boolean forDefrag) { 239 return forDefrag ? Defrag.defragReusableMarkStateThreshold : reusableMarkStateThreshold; 240 } 241 242 /**************************************************************************** 243 * 244 * Allocation 245 */ 246 247 /** 248 * Return a pointer to a set of new usable blocks, or null if none are available. 249 * Use different block selection heuristics depending on whether the allocation 250 * request is "hot" or "cold". 251 * 252 * @param hot True if the requesting context is for hot allocations (used for 253 * allocations from high allocation volume sites). 254 * @return The pointer into the alloc table containing usable blocks. 255 */ 256 public Address getSpace(boolean hot, boolean copy, int lineUseCount) { 257 Address rtn; 258 if (copy) 259 defrag.getBlock(); 260 261 linesConsumed += lineUseCount; 262 263 rtn = acquire(PAGES_IN_BLOCK); 264 265 if (VM.VERIFY_ASSERTIONS) { 266 VM.assertions._assert(Block.isAligned(rtn)); 267 VM.assertions._assert(!(copy && Block.isDefragSource(rtn))); 268 } 269 270 if (!rtn.isZero()) { 271 Block.setBlockAsInUse(rtn); 272 Chunk.updateHighWater(rtn); 273 if (VM.VERIFY_ASSERTIONS && Options.verbose.getValue() >= 9) { 274 Log.write("gs["); Log.write(rtn); Log.write(" -> "); Log.write(rtn.plus(BYTES_IN_BLOCK-1)); Log.write(" copy: "); Log.write(copy); Log.writeln("]"); 275 } 276 } 277 278 return rtn; 279 } 280 281 @Override 282 public void growSpace(Address start, Extent bytes, boolean newChunk) { 283 super.growSpace(start, bytes, newChunk); 284 if (newChunk) { 285 Address chunk = chunkAlign(start.plus(bytes), true); 286 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(chunkAlign(start.plus(bytes), true).EQ(chunk)); 287 Chunk.clearMetaData(chunk); 288 chunkMap.addNewChunkToMap(chunk); 289 } 290 } 291 292 public Address acquireReusableBlocks() { 293 if (VM.VERIFY_ASSERTIONS) { 294 VM.assertions._assert(isRecycleAllocChunkAligned(allocBlockCursor)); 295 VM.assertions._assert(isRecycleAllocChunkAligned(allocBlockSentinel)); 296 } 297 Address rtn; 298 299 lock(); 300 if (exhaustedReusableSpace) 301 rtn = Address.zero(); 302 else { 303 rtn = allocBlockCursor; 304 Address lastAllocChunk = chunkAlign(allocBlockCursor, true); 305 allocBlockCursor = allocBlockCursor.plus(BYTES_IN_RECYCLE_ALLOC_CHUNK); 306 if (allocBlockCursor.GT(Chunk.getHighWater(lastAllocChunk))) 307 allocBlockCursor = chunkMap.nextChunk(lastAllocChunk); 308 if (VM.VERIFY_ASSERTIONS && Options.verbose.getValue() >= 9) { 309 Log.write("arb[ rtn: "); Log.write(rtn); Log.write(" allocBlockCursor: "); Log.write(allocBlockCursor); Log.write(" allocBlockSentinel: "); Log.write(allocBlockSentinel); Log.writeln("]"); 310 } 311 312 if (allocBlockCursor.isZero() || allocBlockCursor.EQ(allocBlockSentinel)) { 313 exhaustedReusableSpace = true; 314 if (VM.VERIFY_ASSERTIONS && Options.verbose.getValue() >= 9) { 315 Log.writeln("[Reusable space exhausted]"); 316 } 317 } 318 } 319 unlock(); 320 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(isRecycleAllocChunkAligned(rtn)); 321 return rtn; 322 } 323 324 /** 325 * Release a block. A block is free, so call the underlying page allocator 326 * to release the associated storage. 327 * 328 * @param block The address of the block to be released 329 */ 330 @Override 331 @Inline 332 public void release(Address block) { 333 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(Block.isAligned(block)); 334 Block.setBlockAsUnallocated(block); 335 ((FreeListPageResource) pr).releasePages(block); 336 } 337 338 /** 339 * {@inheritDoc}<p> 340 * 341 * This hook is called by the page level allocators whenever a 342 * complete discontiguous chunk is released. 343 */ 344 @Override 345 public int releaseDiscontiguousChunks(Address chunk) { 346 chunkMap.removeChunkFromMap(chunk); 347 return super.releaseDiscontiguousChunks(chunk); 348 } 349 350 /**************************************************************************** 351 * 352 * Header manipulation 353 */ 354 355 /** 356 * Perform any required post allocation initialization 357 * 358 * @param object the object ref to the storage to be initialized 359 */ 360 @Inline 361 public void postAlloc(ObjectReference object, int bytes) { 362 if (bytes > BYTES_IN_LINE) 363 ObjectHeader.markAsStraddling(object); 364 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(ObjectHeader.isNewObject(object)); 365 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(!ForwardingWord.isForwardedOrBeingForwarded(object)); 366 } 367 368 /** 369 * Perform any required post copy (i.e. in-GC allocation) initialization. 370 * This is relevant (for example) when Squish is used as the mature space in 371 * a copying GC. 372 * 373 * @param object the object ref to the storage to be initialized 374 * @param majorGC Is this copy happening during a major gc? 375 */ 376 @Inline 377 public void postCopy(ObjectReference object, int bytes, boolean majorGC) { 378 ObjectHeader.writeMarkState(object, markState, bytes > BYTES_IN_LINE); 379 if (!MARK_LINE_AT_SCAN_TIME && majorGC) markLines(object); 380 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(!ForwardingWord.isForwardedOrBeingForwarded(object)); 381 if (VM.VERIFY_ASSERTIONS && HeaderByte.NEEDS_UNLOGGED_BIT) VM.assertions._assert(HeaderByte.isUnlogged(object)); 382 } 383 384 /**************************************************************************** 385 * 386 * Object tracing 387 */ 388 389 /** 390 * Trace a reference to an object. If the object header is not already 391 * marked, mark the object and enqueue it for subsequent processing. 392 * 393 * @param trace The trace performing the transitive closure 394 * @param object The object to be traced. 395 * @param allocator The allocator to which any copying should be directed 396 * @return The object, which may have been moved. 397 */ 398 @Inline 399 public ObjectReference traceObject(TransitiveClosure trace, ObjectReference object, int allocator) { 400 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(defrag.determined(true)); 401 402 ObjectReference rtn = object; 403 if (isDefragSource(object)) 404 rtn = traceObjectWithOpportunisticCopy(trace, object, allocator, false); 405 else 406 traceObjectWithoutMoving(trace, object); 407 408 if (VM.VERIFY_ASSERTIONS) { 409 VM.assertions._assert(!rtn.isNull()); 410 VM.assertions._assert(defrag.spaceExhausted() || !isDefragSource(rtn) || (ObjectHeader.isPinnedObject(rtn))); 411 } 412 return rtn; 413 } 414 415 /** 416 * Trace a reference to an object in the context of a non-moving collection. This 417 * call is optimized for the simpler non-moving case. 418 * 419 * @param trace The trace performing the transitive closure 420 * @param object The object to be traced. 421 * @return The object (there is no object forwarding in this 422 * trace method, so we always return the same object: this could be a 423 * void method but for compliance to a more general interface). 424 */ 425 @Inline 426 public ObjectReference fastTraceObject(TransitiveClosure trace, ObjectReference object) { 427 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(defrag.determined(false)); 428 traceObjectWithoutMoving(trace, object); 429 return object; 430 } 431 432 /** 433 * Trace a reference to an object during a nursery collection for 434 * a sticky mark bits implementation of immix. If the object header 435 * is not already marked, mark the object and enqueue it for subsequent 436 * processing. 437 * 438 * @param trace The trace performing the transitive closure 439 * @param object The object to be traced. 440 * @param allocator The allocator to which any copying should be directed 441 * @return Either the object or a forwarded object, depending on 442 * the policy in place. 443 */ 444 @Inline 445 public ObjectReference nurseryTraceObject(TransitiveClosure trace, ObjectReference object, int allocator) { 446 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(!defrag.inDefrag()); 447 if (ObjectHeader.isMatureObject(object)) 448 return object; 449 else if (PREFER_COPY_ON_NURSERY_GC) 450 return traceObjectWithOpportunisticCopy(trace, object, allocator, true); 451 else 452 return fastTraceObject(trace, object); 453 } 454 455 /** 456 * Trace a reference to an object. This interface is not supported by immix, since 457 * we require the allocator to be identified except for the special case of the fast 458 * trace. 459 * 460 * @param trace The trace performing the transitive closure 461 * @param object The object to be traced. 462 * @return null and fail. 463 */ 464 @Override 465 public ObjectReference traceObject(TransitiveClosure trace, ObjectReference object) { 466 VM.assertions.fail("unsupported interface"); 467 return null; 468 } 469 470 /** 471 * Trace a reference to an object in the context of a non-moving collection. This 472 * call is optimized for the simpler non-moving case. 473 * 474 * @param trace The trace performing the transitive closure 475 * @param object The object to be traced. 476 */ 477 @Inline 478 private void traceObjectWithoutMoving(TransitiveClosure trace, ObjectReference object) { 479 byte markValue = markState; 480 byte oldMarkState = ObjectHeader.testAndMark(object, markValue); 481 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(!defrag.inDefrag() || defrag.spaceExhausted() || !isDefragSource(object)); 482 if (oldMarkState != markValue) { 483 if (!MARK_LINE_AT_SCAN_TIME) 484 markLines(object); 485 trace.processNode(object); 486 } 487 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(!ForwardingWord.isForwardedOrBeingForwarded(object)); 488 if (VM.VERIFY_ASSERTIONS && HeaderByte.NEEDS_UNLOGGED_BIT) VM.assertions._assert(HeaderByte.isUnlogged(object)); 489 } 490 491 /** 492 * Trace a reference to an object, forwarding the object if appropriate 493 * If the object is not already marked, mark the object and enqueue it 494 * for subsequent processing. 495 * 496 * @param trace The trace performing the transitive closure 497 * @param object The object to be traced. 498 * @param allocator The allocator to which any copying should be directed 499 * @return Either the object or a forwarded object, if it was forwarded. 500 */ 501 @Inline 502 private ObjectReference traceObjectWithOpportunisticCopy(TransitiveClosure trace, ObjectReference object, int allocator, boolean nurseryCollection) { 503 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert((nurseryCollection && !ObjectHeader.isMatureObject(object)) || (defrag.determined(true) && isDefragSource(object))); 504 505 /* Race to be the (potential) forwarder */ 506 Word priorStatusWord = ForwardingWord.attemptToForward(object); 507 if (ForwardingWord.stateIsForwardedOrBeingForwarded(priorStatusWord)) { 508 /* We lost the race; the object is either forwarded or being forwarded by another thread. */ 509 /* Note that the concurrent attempt to forward the object may fail, so the object may remain in-place */ 510 ObjectReference rtn = ForwardingWord.spinAndGetForwardedObject(object, priorStatusWord); 511 if (VM.VERIFY_ASSERTIONS && rtn == object) VM.assertions._assert((nurseryCollection && ObjectHeader.testMarkState(object, markState)) || defrag.spaceExhausted() || ObjectHeader.isPinnedObject(object)); 512 if (VM.VERIFY_ASSERTIONS && rtn != object) VM.assertions._assert(nurseryCollection || !isDefragSource(rtn)); 513 if (VM.VERIFY_ASSERTIONS && HeaderByte.NEEDS_UNLOGGED_BIT) VM.assertions._assert(HeaderByte.isUnlogged(rtn)); 514 return rtn; 515 } else { 516 byte priorState = (byte) (priorStatusWord.toInt() & 0xFF); 517 /* the object is unforwarded, either because this is the first thread to reach it, or because the object can't be forwarded */ 518 if (ObjectHeader.testMarkState(priorState, markState)) { 519 /* the object has not been forwarded, but has the correct mark state; unlock and return unmoved object */ 520 /* Note that in a sticky mark bits collector, the mark state does not change at each GC, so correct mark state does not imply another thread got there first */ 521 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(nurseryCollection || defrag.spaceExhausted() || ObjectHeader.isPinnedObject(object)); 522 ObjectHeader.returnToPriorStateAndEnsureUnlogged(object, priorState); // return to uncontested state 523 if (VM.VERIFY_ASSERTIONS && Plan.NEEDS_LOG_BIT_IN_HEADER) VM.assertions._assert(HeaderByte.isUnlogged(object)); 524 return object; 525 } else { 526 /* we are the first to reach the object; either mark in place or forward it */ 527 ObjectReference newObject; 528 if (ObjectHeader.isPinnedObject(object) || (!nurseryCollection && defrag.spaceExhausted())) { 529 /* mark in place */ 530 ObjectHeader.setMarkStateUnlogAndUnlock(object, priorState, markState); 531 newObject = object; 532 if (VM.VERIFY_ASSERTIONS && Plan.NEEDS_LOG_BIT_IN_HEADER) VM.assertions._assert(HeaderByte.isUnlogged(newObject)); 533 } else { 534 /* forward */ 535 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(!ObjectHeader.isPinnedObject(object)); 536 newObject = ForwardingWord.forwardObject(object, allocator); 537 if (VM.VERIFY_ASSERTIONS && Plan.NEEDS_LOG_BIT_IN_HEADER) VM.assertions._assert(HeaderByte.isUnlogged(newObject)); 538 } 539 if (VM.VERIFY_ASSERTIONS && Options.verbose.getValue() >= 9) { 540 Log.write("C["); Log.write(object); Log.write("/"); 541 Log.write(getName()); Log.write("] -> "); 542 Log.write(newObject); Log.write("/"); 543 Log.write(Space.getSpaceForObject(newObject).getName()); 544 Log.writeln("]"); 545 } 546 if (!MARK_LINE_AT_SCAN_TIME) 547 markLines(newObject); 548 trace.processNode(newObject); 549 if (VM.VERIFY_ASSERTIONS) { 550 if (!((getSpaceForObject(newObject) != this) || 551 (newObject == object) || 552 (nurseryCollection && willNotMoveThisNurseryGC(newObject)) || 553 (defrag.inDefrag() && willNotMoveThisGC(newObject)) 554 )) { 555 Log.write(" object: "); Log.writeln(object); 556 Log.write("newObject: "); Log.writeln(newObject); 557 Log.write(" space: "); Log.writeln(getName()); 558 Log.write(" nursery?: "); Log.writeln(nurseryCollection); 559 Log.write(" mature?: "); Log.writeln(ObjectHeader.isMatureObject(object)); 560 Log.write(" wnmngc?: "); Log.writeln(willNotMoveThisNurseryGC(newObject)); 561 Log.write(" pinned?: "); Log.writeln(ObjectHeader.isPinnedObject(object)); 562 Space otherSpace = getSpaceForObject(newObject); 563 Log.write(" space(o): "); Log.writeln(otherSpace == null ? "<NULL>" : otherSpace.getName()); 564 VM.assertions._assert(false); 565 } 566 } 567 return newObject; 568 } 569 } 570 } 571 572 /** 573 * Mark the line/s associated with a given object. This is distinct from the 574 * above tracing code because line marks are stored separately from the 575 * object headers (thus both must be set), and also because we found empirically 576 * that it was more efficient to perform the line mark of the object during 577 * the scan phase (which occurs after the trace phase), presumably because 578 * the latency of the associated memory operations was better hidden in the 579 * context of that code 580 * 581 * @param object The object which is live and for which the associated lines 582 * must be marked. 583 */ 584 public void markLines(ObjectReference object) { 585 Address address = VM.objectModel.objectStartRef(object); 586 Line.mark(address, lineMarkState); 587 if (ObjectHeader.isStraddlingObject(object)) 588 Line.markMultiLine(address, object, lineMarkState); 589 } 590 591 public int getNextUnavailableLine(Address baseLineAvailAddress, int line) { 592 return Line.getNextUnavailable(baseLineAvailAddress, line, lineUnavailState); 593 } 594 595 public int getNextAvailableLine(Address baseLineAvailAddress, int line) { 596 return Line.getNextAvailable(baseLineAvailAddress, line, lineUnavailState); 597 } 598 599 /**************************************************************************** 600 * 601 * Establish available lines 602 */ 603 604 /** 605 * Establish the number of recyclable lines lines available for allocation 606 * during defragmentation, populating the spillAvailHistogram, which buckets 607 * available lines according to the number of holes on the block on which 608 * the available lines reside. 609 * 610 * @param spillAvailHistogram A histogram of availability to be populated 611 * @return The number of available recyclable lines 612 */ 613 int getAvailableLines(int[] spillAvailHistogram) { 614 int availableLines; 615 if (allocBlockCursor.isZero() || exhaustedReusableSpace) { 616 availableLines = 0; 617 } else { 618 if (allocBlockCursor.EQ(allocBlockSentinel)) { 619 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(!exhaustedReusableSpace); 620 allocBlockCursor = chunkMap.getHeadChunk(); 621 allocBlockSentinel = allocBlockCursor; 622 } 623 availableLines = getUsableLinesInRegion(allocBlockCursor, allocBlockSentinel, spillAvailHistogram); 624 } 625 return availableLines; 626 } 627 628 /** 629 * Return the number of lines usable for allocation during defragmentation in the 630 * address range specified by start and end. Populate a histogram to indicate where 631 * the usable lines reside as a function of block hole count. 632 * 633 * @param start The start of the region to be checked for availability 634 * @param end The end of the region to be checked for availability 635 * @param spillAvailHistogram The histogram which will be populated 636 * @return The number of usable lines 637 */ 638 private int getUsableLinesInRegion(Address start, Address end, int[] spillAvailHistogram) { 639 int usableLines = 0; 640 Address blockCursor = Chunk.isAligned(start) ? start.plus(Chunk.FIRST_USABLE_BLOCK_INDEX<<LOG_BYTES_IN_BLOCK) : start; 641 Address blockStateCursor = Block.getBlockMarkStateAddress(blockCursor); 642 Address chunkCursor = Chunk.align(blockCursor); 643 if (Chunk.getByteOffset(end) < Chunk.FIRST_USABLE_BLOCK_INDEX<<LOG_BYTES_IN_BLOCK) 644 end = Chunk.align(end).plus(Chunk.FIRST_USABLE_BLOCK_INDEX<<LOG_BYTES_IN_BLOCK); 645 646 for (int i = 0; i <= MAX_CONSV_SPILL_COUNT; i++) spillAvailHistogram[i] = 0; 647 648 Address highwater = Chunk.getHighWater(chunkCursor); 649 do { 650 short markState = blockStateCursor.loadShort(); 651 if (markState != 0 && markState <= reusableMarkStateThreshold) { 652 int usable = LINES_IN_BLOCK - markState; 653 short bucket = Block.getConservativeSpillCount(blockCursor); 654 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(bucket >= 0 && bucket <= MAX_CONSV_SPILL_COUNT); 655 spillAvailHistogram[bucket] += usable; 656 usableLines += usable; 657 } 658 blockCursor = blockCursor.plus(BYTES_IN_BLOCK); 659 if (blockCursor.GT(highwater)) { 660 chunkCursor = chunkMap.nextChunk(chunkCursor); 661 if (chunkCursor.isZero()) break; 662 blockCursor = chunkCursor.plus(Chunk.FIRST_USABLE_BLOCK_INDEX<<LOG_BYTES_IN_BLOCK); 663 blockStateCursor = Block.getBlockMarkStateAddress(blockCursor); 664 highwater = Chunk.getHighWater(chunkCursor); 665 } else 666 blockStateCursor = blockStateCursor.plus(Block.BYTES_IN_BLOCK_STATE_ENTRY); 667 } while (blockCursor.NE(end)); 668 669 return usableLines; 670 } 671 672 /**************************************************************************** 673 * 674 * Object state 675 */ 676 677 /** 678 * Generic test of the liveness of an object 679 * 680 * @param object The object in question 681 * @return {@code true} if this object is known to be live (i.e. it is marked) 682 */ 683 @Override 684 @Inline 685 public boolean isLive(ObjectReference object) { 686 if (defrag.inDefrag() && isDefragSource(object)) 687 return ForwardingWord.isForwardedOrBeingForwarded(object) || ObjectHeader.testMarkState(object, markState); 688 else 689 return ObjectHeader.testMarkState(object, markState); 690 } 691 692 /** 693 * Test the liveness of an object during copying sticky mark bits collection 694 * 695 * @param object The object in question 696 * @return True if this object is known to be live (i.e. it is marked) 697 */ 698 @Inline 699 public boolean copyNurseryIsLive(ObjectReference object) { 700 return ForwardingWord.isForwardedOrBeingForwarded(object) || ObjectHeader.testMarkState(object, markState); 701 } 702 703 /** 704 * Test the liveness of an object during defragmentation 705 * 706 * @param object The object in question 707 * @return {@code true} if this object is known to be live (i.e. it is marked) 708 */ 709 @Inline 710 public boolean fastIsLive(ObjectReference object) { 711 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(!defrag.inDefrag()); 712 return ObjectHeader.testMarkState(object, markState); 713 } 714 715 @Inline 716 public boolean willNotMoveThisGC(ObjectReference object) { 717 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(getSpaceForObject(object) == this && defrag.inDefrag()); 718 return ObjectHeader.isPinnedObject(object) || willNotMoveThisGC(VM.objectModel.refToAddress(object)); 719 } 720 721 @Inline 722 public boolean willNotMoveThisNurseryGC(ObjectReference object) { 723 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(getSpaceForObject(object) == this); 724 return ObjectHeader.isMatureObject(object); 725 } 726 727 @Inline 728 private boolean isDefragSource(ObjectReference object) { 729 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(getSpaceForObject(object) == this); 730 return isDefragSource(VM.objectModel.refToAddress(object)); 731 } 732 733 @Inline 734 public boolean willNotMoveThisGC(Address address) { 735 return !defrag.inDefrag() || defrag.spaceExhausted() || !isDefragSource(address); 736 } 737 738 @Inline 739 public boolean isDefragSource(Address address) { 740 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(getSpaceForObject(address.toObjectReference()) == this); 741 return Block.isDefragSource(address); 742 } 743 744 745 /**************************************************************************** 746 * 747 * Locks 748 */ 749 750 /** 751 * Acquire the appropriate lock depending on whether the context is 752 * GC or mutator. 753 */ 754 private void lock() { 755 if (inCollection) 756 gcLock.acquire(); 757 else 758 mutatorLock.acquire(); 759 } 760 761 /** 762 * Release the appropriate lock depending on whether the context is 763 * GC or mutator. 764 */ 765 private void unlock() { 766 if (inCollection) 767 gcLock.release(); 768 else 769 mutatorLock.release(); 770 } 771 772 773 /**************************************************************************** 774 * 775 * Misc 776 */ 777 778 /** 779 * 780 */ 781 public static boolean isRecycleAllocChunkAligned(Address ptr) { 782 return ptr.toWord().and(RECYCLE_ALLOC_CHUNK_MASK).EQ(Word.zero()); 783 } 784 785 ChunkList getChunkMap() { return chunkMap; } 786 Defrag getDefrag() { return defrag; } 787 }