/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Eclipse Public License (EPL); * You may not use this file except in compliance with the License. You * may obtain a copy of the License at * * http://www.opensource.org/licenses/eclipse-1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.mmtk.policy; import org.mmtk.utility.alloc.BlockAllocator; import org.mmtk.utility.alloc.EmbeddedMetaData; import org.mmtk.utility.heap.FreeListPageResource; import org.mmtk.utility.heap.Map; import org.mmtk.utility.heap.VMRequest; import org.mmtk.utility.Constants; import org.mmtk.utility.Conversions; import org.mmtk.utility.Memory; import org.mmtk.vm.Lock; import org.mmtk.vm.VM; import org.vmmagic.pragma.*; import org.vmmagic.unboxed.*; /** * Each instance of this class corresponds to one mark-sweep *space*. * Each of the instance methods of this class may be called by any * thread (i.e. synchronization must be explicit in any instance or * class method). This contrasts with the MarkSweepLocal, where * instances correspond to *plan* instances and therefore to kernel * threads. Thus unlike this class, synchronization is not necessary * in the instance methods of MarkSweepLocal. */ @Uninterruptible public abstract class SegregatedFreeListSpace extends Space implements Constants { /**************************************************************************** * * Class variables */ protected static final boolean LAZY_SWEEP = true; private static final boolean COMPACT_SIZE_CLASSES = false; protected static final int MIN_CELLS = 6; protected static final int MAX_CELLS = 99; // (1<<(INUSE_BITS-1))-1; protected static final int MAX_CELL_SIZE = 8<<10; public static final int MAX_FREELIST_OBJECT_BYTES = MAX_CELL_SIZE; // live bits etc private static final int OBJECT_LIVE_SHIFT = LOG_MIN_ALIGNMENT; // 4 byte resolution private static final int LOG_BIT_COVERAGE = OBJECT_LIVE_SHIFT; private static final int LOG_LIVE_COVERAGE = LOG_BIT_COVERAGE + LOG_BITS_IN_BYTE; private static final int LIVE_BYTES_PER_REGION = 1 << (EmbeddedMetaData.LOG_BYTES_IN_REGION - LOG_LIVE_COVERAGE); private static final Word WORD_SHIFT_MASK = Word.one().lsh(LOG_BITS_IN_WORD).minus(Extent.one()); private static final int LOG_LIVE_WORD_STRIDE = LOG_LIVE_COVERAGE + LOG_BYTES_IN_WORD; private static final Extent LIVE_WORD_STRIDE = Extent.fromIntSignExtend(1<<LOG_LIVE_WORD_STRIDE); private static final Word LIVE_WORD_STRIDE_MASK = LIVE_WORD_STRIDE.minus(1).toWord().not(); private static final int NET_META_DATA_BYTES_PER_REGION = BlockAllocator.META_DATA_BYTES_PER_REGION + LIVE_BYTES_PER_REGION; protected static final int META_DATA_PAGES_PER_REGION_WITH_BITMAP = Conversions.bytesToPages(Extent.fromIntSignExtend(NET_META_DATA_BYTES_PER_REGION)); protected static final int META_DATA_PAGES_PER_REGION_NO_BITMAP = Conversions.bytesToPages(Extent.fromIntSignExtend(BlockAllocator.META_DATA_BYTES_PER_REGION)); private static final Extent META_DATA_OFFSET = BlockAllocator.META_DATA_EXTENT; // calculate worst case fragmentation very conservatively private static final int NEW_SIZECLASS_OVERHEAD = sizeClassCount(); // one page wasted per size class private static final int METADATA_OVERHEAD = META_DATA_PAGES_PER_REGION_WITH_BITMAP; // worst case scenario public static final float WORST_CASE_FRAGMENTATION = 1 + ((NEW_SIZECLASS_OVERHEAD + METADATA_OVERHEAD)/(float) EmbeddedMetaData.BYTES_IN_REGION); /**************************************************************************** * * Instance variables */ protected final Lock lock = VM.newLock("SegregatedFreeListGlobal"); protected final AddressArray consumedBlockHead = AddressArray.create(sizeClassCount()); protected final AddressArray flushedBlockHead = AddressArray.create(sizeClassCount()); protected final AddressArray availableBlockHead = AddressArray.create(sizeClassCount()); private final int[] cellSize = new int[sizeClassCount()]; private final byte[] blockSizeClass = new byte[sizeClassCount()]; private final int[] blockHeaderSize = new int[sizeClassCount()]; /** * The caller specifies the region of virtual memory to be used for * this space. If this region conflicts with an existing space, * then the constructor will fail. * * @param name The name of this space (used when printing error messages etc) * @param pageBudget The number of pages this space may consume before consulting the plan * @param additionalMetadata The number of meta data bytes per region for the subclass. * @param vmRequest An object describing the virtual memory requested. */ public SegregatedFreeListSpace(String name, int pageBudget, int additionalMetadata, VMRequest vmRequest) { super(name, false, false, vmRequest); initSizeClasses(); int totalMetadata = additionalMetadata; if (maintainSideBitmap()) { totalMetadata += META_DATA_PAGES_PER_REGION_WITH_BITMAP; } else { totalMetadata += META_DATA_PAGES_PER_REGION_NO_BITMAP; } if (vmRequest.isDiscontiguous()) { pr = new FreeListPageResource(pageBudget, this, totalMetadata); } else { pr = new FreeListPageResource(pageBudget, this, start, extent, totalMetadata); } } /** * Should SegregatedFreeListSpace manage a side bitmap to keep track of live objects? */ protected abstract boolean maintainSideBitmap(); /** * Do we need to preserve free lists as we move blocks around. */ protected abstract boolean preserveFreeList(); /**************************************************************************** * * Allocation */ /** * Return a block to the global pool. * * @param block The block to return * @param sizeClass The size class */ public void returnConsumedBlock(Address block, int sizeClass) { returnBlock(block, sizeClass, Address.zero()); } /** * Return a block to the global pool. * * @param block The block to return * @param sizeClass The size class * @param freeCell The first free cell in the block. */ public void returnBlock(Address block, int sizeClass, Address freeCell) { if (VM.VERIFY_ASSERTIONS) { VM.assertions._assert(BlockAllocator.getNext(block).isZero()); } if (preserveFreeList()) { setFreeList(block, freeCell); } lock.acquire(); BlockAllocator.setNext(block, consumedBlockHead.get(sizeClass)); consumedBlockHead.set(sizeClass, block); lock.release(); } /** * Acquire a new block from the global pool to allocate into. This method * with either return a non-empty free list, or zero when allocation * fails. * * This method will populate the passed in free list for the given size * class and return the address of the block. * * @param sizeClass The size class to allocate into * @param freeList The free list to populate * @return The address of the block */ public Address getAllocationBlock(int sizeClass, AddressArray freeList) { lock.acquire(); Address block; while(!(block = availableBlockHead.get(sizeClass)).isZero()) { availableBlockHead.set(sizeClass, BlockAllocator.getNext(block)); lock.release(); /* This block is no longer on any list */ BlockAllocator.setNext(block, Address.zero()); /* Can we allocate into this block? */ Address cell = advanceToBlock(block, sizeClass); if (!cell.isZero()) { freeList.set(sizeClass, cell); return block; } /* Block was full */ lock.acquire(); BlockAllocator.setNext(block, consumedBlockHead.get(sizeClass)); consumedBlockHead.set(sizeClass, block); } lock.release(); return expandSizeClass(sizeClass, freeList); } /** * Expand a particular size class, allocating a new block, breaking * the block into cells and placing those cells on a free list for * that block. The block becomes the current head for this size * class and the address of the first available cell is returned.<p> * * <b>This is guaranteed to return pre-zeroed cells</b> * * @param sizeClass The size class to be expanded * @param freeList The free list to populate. * @return The block that was just allocated. */ @Inline private Address expandSizeClass(int sizeClass, AddressArray freeList) { Address block = BlockAllocator.alloc(this, blockSizeClass[sizeClass]); if (block.isZero()) { return Address.zero(); } BlockAllocator.setNext(block, Address.zero()); BlockAllocator.setAllClientSizeClass(block, blockSizeClass[sizeClass], (byte) sizeClass); notifyNewBlock(block, sizeClass); int cellExtent = cellSize[sizeClass]; int blockSize = BlockAllocator.blockSize(blockSizeClass[sizeClass]); int useableBlockSize = blockSize - blockHeaderSize[sizeClass]; Address firstCell = block.plus(blockHeaderSize[sizeClass]); Address sentinel = block.plus(blockSize); /* pre-zero the block */ VM.memory.zero(firstCell, Extent.fromIntZeroExtend(useableBlockSize)); /* construct the free list */ Address nextCell; Address cell = firstCell; while ((nextCell = cell.plus(cellExtent)).LT(sentinel)) { cell.store(nextCell); cell = nextCell; } /* Populate the free list */ freeList.set(sizeClass, firstCell); return block; } /**************************************************************************** * * Block management */ /** * Initialize the size class data structures. */ private void initSizeClasses() { for (int sc = 0; sc < sizeClassCount(); sc++) { cellSize[sc] = getBaseCellSize(sc); for (byte blk = 0; blk < BlockAllocator.BLOCK_SIZE_CLASSES; blk++) { int usableBytes = BlockAllocator.blockSize(blk); int cells = usableBytes / cellSize[sc]; blockSizeClass[sc] = blk; /* cells must start at multiple of MIN_ALIGNMENT because cellSize is also supposed to be multiple, this should do the trick: */ blockHeaderSize[sc] = BlockAllocator.blockSize(blk) - cells * cellSize[sc]; if (((usableBytes < BYTES_IN_PAGE) && (cells*2 > MAX_CELLS)) || ((usableBytes > (BYTES_IN_PAGE>>1)) && (cells > MIN_CELLS))) break; } } } /** * Get the size class for a given number of bytes. * * We use size classes based on a worst case internal fragmentation * loss target of 1/8. In fact, across sizes from 8 bytes to 512 * the average worst case loss is 13.3%, giving an expected loss * (assuming uniform distribution) of about 7%. We avoid using the * Lea class sizes because they were so numerous and therefore * liable to lead to excessive inter-class-size fragmentation.<p> * * This method may segregate arrays and scalars (currently it does * not).<p> * * This method should be more intelligent and take alignment requests * into consideration. The issue with this is that the block header * which can be varied by subclasses can change the alignment of the * cells.<p> * * @param bytes The number of bytes required to accommodate the object * to be allocated. * @return The size class capable of accommodating the allocation request. */ @Inline public final int getSizeClass(int bytes) { if (VM.VERIFY_ASSERTIONS) VM.assertions._assert((bytes > 0) && (bytes <= MAX_CELL_SIZE)); int sz1 = bytes - 1; if (BYTES_IN_ADDRESS == 4) { // 32-bit if (COMPACT_SIZE_CLASSES) return ( (sz1 <= 31) ? (sz1 >> 2) : // 4 bytes apart (sz1 <= 63) ? 4 + (sz1 >> 3) : // 8 bytes apart (sz1 <= 95) ? 8 + (sz1 >> 4) : // 16 bytes apart (sz1 <= 223) ? 14 + (sz1 >> 6) : // 64 bytes apart (sz1 <= 734) ? 17 + (sz1 >> 8) : // 256 bytes apart 20 + (sz1 >> 10)); // 1024 bytes apart else return ( (sz1 <= 63) ? (sz1 >> 2) : // 4 bytes apart (sz1 <= 127) ? 12 + (sz1 >> 4) : // 16 bytes apart (sz1 <= 255) ? 16 + (sz1 >> 5) : // 32 bytes apart (sz1 <= 511) ? 20 + (sz1 >> 6) : // 64 bytes apart (sz1 <= 2047) ? 26 + (sz1 >> 8) : // 256 bytes apart 32 + (sz1 >> 10)); // 1024 bytes apart } else { // 64-bit if (COMPACT_SIZE_CLASSES) return ( (sz1 <= 95) ? (sz1 >> 3) : // 8 bytes apart (sz1 <= 127) ? 6 + (sz1 >> 4) : // 16 bytes apart (sz1 <= 191) ? 10 + (sz1 >> 5) : // 32 bytes apart (sz1 <= 383) ? 13 + (sz1 >> 6) : // 64 bytes apart (sz1 <= 511) ? 16 + (sz1 >> 7) : // 128 bytes apart (sz1 <= 1023) ? 19 + (sz1 >> 9) : // 512 bytes apart 20 + (sz1 >> 10)); // 1024 bytes apart else return ( (sz1 <= 111) ? (sz1 >> 3) : // 8 bytes apart (sz1 <= 223) ? 7 + (sz1 >> 4) : // 16 bytes apart (sz1 <= 319) ? 14 + (sz1 >> 5) : // 32 bytes apart (sz1 <= 575) ? 19 + (sz1 >> 6) : // 64 bytes apart (sz1 <= 2047) ? 26 + (sz1 >> 8) : // 256 bytes apart 32 + (sz1 >> 10)); // 1024 bytes apart } } /** * Return the size of a basic cell (i.e. not including any cell * header) for a given size class. * * @param sc The size class in question * @return The size of a basic cell (i.e. not including any cell * header). */ @Inline public final int getBaseCellSize(int sc) { if (VM.VERIFY_ASSERTIONS) VM.assertions._assert((sc >= 0) && (sc < sizeClassCount())); if (BYTES_IN_ADDRESS == 4) { // 32-bit if (COMPACT_SIZE_CLASSES) return ((sc < 8) ? (sc + 1) << 2: (sc < 12) ? (sc - 3) << 3: (sc < 16) ? (sc - 7) << 4: (sc < 18) ? (sc - 13) << 6: (sc < 21) ? (sc - 16) << 8: (sc - 19) << 10); else return ((sc < 16) ? (sc + 1) << 2: (sc < 20) ? (sc - 11) << 4: (sc < 24) ? (sc - 15) << 5: (sc < 28) ? (sc - 19) << 6: (sc < 34) ? (sc - 25) << 8: (sc - 31) << 10); } else { // 64-bit if (COMPACT_SIZE_CLASSES) return ((sc < 12) ? (sc + 1) << 3: (sc < 14) ? (sc - 5) << 4: (sc < 16) ? (sc - 9) << 5: (sc < 19) ? (sc - 12) << 6: (sc < 20) ? (sc - 15) << 7: (sc < 21) ? (sc - 18) << 9: (sc - 19) << 10); else return ((sc < 14) ? (sc + 1) << 3: (sc < 21) ? (sc - 6) << 4: (sc < 24) ? (sc - 13) << 5: (sc < 28) ? (sc - 18) << 6: (sc < 34) ? (sc - 25) << 8: (sc - 31) << 10); } } /** * The number of distinct size classes. */ @Inline public static int sizeClassCount() { return (COMPACT_SIZE_CLASSES) ? 28 : 40; } /**************************************************************************** * * Preserving (saving & restoring) free lists */ /** * Prepare a block for allocation, returning a free list into the block. * * @param block The new block * @param sizeClass The block's sizeclass. */ protected abstract Address advanceToBlock(Address block, int sizeClass); /** * Notify that a new block has been installed. * * @param block The new block * @param sizeClass The block's sizeclass. */ protected void notifyNewBlock(Address block, int sizeClass) {} /** * Should the sweep reclaim the cell containing this object. Is this object * live. This is only used when maintainSideBitmap is false. * * @param object The object to query * @return True if the cell should be reclaimed */ protected boolean reclaimCellForObject(ObjectReference object) { VM.assertions.fail("Must implement reclaimCellForObject if not maintaining side bitmap"); return false; } /**************************************************************************** * * Metadata manipulation */ /** * In the case where free lists associated with each block are * preserved, get the free list for a given block. * * @param block The block whose free list is to be found * @return The free list for this block */ @Inline protected final Address getFreeList(Address block) { if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(preserveFreeList()); return BlockAllocator.getFreeListMeta(block); } /** * In the case where free lists associated with each block are * preserved, set the free list for a given block. * * @param block The block whose free list is to be found * @param cell The head of the free list (i.e. the first cell in the * free list). */ @Inline protected final void setFreeList(Address block, Address cell) { if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(preserveFreeList()); BlockAllocator.setFreeListMeta(block, cell); } /**************************************************************************** * * Collection */ /** * Clear all block marks for this space. This method is important when * it is desirable to do partial collections, which man mean that block * marks need to be explicitly cleared when necessary. */ protected final void clearAllBlockMarks() { if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(!maintainSideBitmap()); for (int sizeClass = 0; sizeClass < sizeClassCount(); sizeClass++) { Extent blockSize = Extent.fromIntSignExtend(BlockAllocator.blockSize(blockSizeClass[sizeClass])); /* Flushed blocks */ Address block = flushedBlockHead.get(sizeClass); while (!block.isZero()) { Address next = BlockAllocator.getNext(block); clearBlockMark(block, blockSize); block = next; } /* Available blocks */ block = consumedBlockHead.get(sizeClass); while (!block.isZero()) { Address next = BlockAllocator.getNext(block); clearBlockMark(block, blockSize); block = next; } } } /** * Sweep all blocks for free objects. * * @param clearMarks should we clear block mark bits as we process. */ protected final void sweepConsumedBlocks(boolean clearMarks) { for (int sizeClass = 0; sizeClass < sizeClassCount(); sizeClass++) { Extent blockSize = Extent.fromIntSignExtend(BlockAllocator.blockSize(blockSizeClass[sizeClass])); Address availableHead = Address.zero(); /* Flushed blocks */ Address block = flushedBlockHead.get(sizeClass); flushedBlockHead.set(sizeClass, Address.zero()); while (!block.isZero()) { Address next = BlockAllocator.getNext(block); availableHead = sweepBlock(block, sizeClass, blockSize, availableHead, clearMarks); block = next; } /* Consumed blocks */ block = consumedBlockHead.get(sizeClass); consumedBlockHead.set(sizeClass, Address.zero()); while (!block.isZero()) { Address next = BlockAllocator.getNext(block); availableHead = sweepBlock(block, sizeClass, blockSize, availableHead, clearMarks); block = next; } /* Make blocks available */ availableBlockHead.set(sizeClass, availableHead); } } /** * Sweep a block, freeing it and adding to the list given by availableHead * if it contains no free objects. * * @param clearMarks should we clear block mark bits as we process. */ protected final Address sweepBlock(Address block, int sizeClass, Extent blockSize, Address availableHead, boolean clearMarks) { boolean liveBlock = containsLiveCell(block, blockSize, clearMarks); if (!liveBlock) { BlockAllocator.setNext(block, Address.zero()); BlockAllocator.free(this, block); } else { BlockAllocator.setNext(block, availableHead); availableHead = block; if (!LAZY_SWEEP) { setFreeList(block, makeFreeList(block, sizeClass)); } } return availableHead; } /** * Eagerly consume all remaining blocks. */ protected final void consumeBlocks() { for (int sizeClass = 0; sizeClass < sizeClassCount(); sizeClass++) { while (!availableBlockHead.get(sizeClass).isZero()) { Address block = availableBlockHead.get(sizeClass); availableBlockHead.set(sizeClass, BlockAllocator.getNext(block)); advanceToBlock(block, sizeClass); BlockAllocator.setNext(block, consumedBlockHead.get(sizeClass)); consumedBlockHead.set(sizeClass, block); } } } /** * Flush all the allocation blocks to the consumed list. */ protected final void flushAvailableBlocks() { for (int sizeClass = 0; sizeClass < sizeClassCount(); sizeClass++) { flushedBlockHead.set(sizeClass, availableBlockHead.get(sizeClass)); availableBlockHead.set(sizeClass, Address.zero()); } } /** * Does this block contain any live cells. * * @param block The block * @param blockSize The size of the block * @param clearMarks should we clear block mark bits as we process. * @return True if any cells in the block are live */ @Inline protected boolean containsLiveCell(Address block, Extent blockSize, boolean clearMarks) { if (maintainSideBitmap()) { if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(alignToLiveStride(block).EQ(block)); Address cursor = getLiveWordAddress(block); Address sentinel = getLiveWordAddress(block.plus(blockSize.minus(1))); while (cursor.LE(sentinel)) { Word live = cursor.loadWord(); if (!live.isZero()) { return true; } cursor = cursor.plus(BYTES_IN_WORD); } return false; } else { boolean live = false; Address cursor = block; while(cursor.LT(block.plus(blockSize))) { live |= BlockAllocator.checkBlockMeta(cursor); if (clearMarks) BlockAllocator.clearBlockMeta(cursor); cursor = cursor.plus(1 << BlockAllocator.LOG_MIN_BLOCK); } return live; } } /** * Clear block marks for a block * * @param block The block * @param blockSize The size of the block */ @Inline protected void clearBlockMark(Address block, Extent blockSize) { if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(!maintainSideBitmap()); Address cursor = block; while(cursor.LT(block.plus(blockSize))) { BlockAllocator.clearBlockMeta(cursor); cursor = cursor.plus(1 << BlockAllocator.LOG_MIN_BLOCK); } } /** * In the cell containing this object live? * * @param object The object * @return True if the cell is live */ @Inline protected boolean isCellLive(ObjectReference object) { /* Must override if not using the side bitmap */ if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(maintainSideBitmap()); return liveBitSet(object); } /** * Use the live bits for a block to infer free cells and thus * construct a free list for the block. * * @param block The block to be processed * @param sizeClass The size class for the block * @return The head of the new free list */ @Inline protected final Address makeFreeList(Address block, int sizeClass) { Extent blockSize = Extent.fromIntSignExtend(BlockAllocator.blockSize(blockSizeClass[sizeClass])); Address cursor = block.plus(blockHeaderSize[sizeClass]); Address lastFree = Address.zero(); Address firstFree = Address.zero(); Address end = block.plus(blockSize); Extent cellExtent = Extent.fromIntSignExtend(cellSize[sizeClass]); while (cursor.LT(end)) { ObjectReference current = VM.objectModel.getObjectFromStartAddress(cursor); boolean free = true; if (!current.isNull()) { free = !isCellLive(current); } if (free) { if (firstFree.isZero()) { firstFree = cursor; } else { lastFree.store(cursor); } Memory.zeroSmall(cursor, cellExtent); lastFree = cursor; } cursor = cursor.plus(cellExtent); } return firstFree; } /** * Sweep all blocks for free objects. */ public void sweepCells(Sweeper sweeper) { for (int sizeClass = 0; sizeClass < sizeClassCount(); sizeClass++) { Address availableHead = Address.zero(); /* Flushed blocks */ Address block = flushedBlockHead.get(sizeClass); flushedBlockHead.set(sizeClass, Address.zero()); while (!block.isZero()) { Address next = BlockAllocator.getNext(block); availableHead = sweepCells(sweeper, block, sizeClass, availableHead); block = next; } /* Consumed blocks */ block = consumedBlockHead.get(sizeClass); consumedBlockHead.set(sizeClass, Address.zero()); while (!block.isZero()) { Address next = BlockAllocator.getNext(block); availableHead = sweepCells(sweeper, block, sizeClass, availableHead); block = next; } /* Make blocks available */ availableBlockHead.set(sizeClass, availableHead); } } /** * Sweep a block, freeing it and adding to the list given by availableHead * if it contains no free objects. */ private Address sweepCells(Sweeper sweeper, Address block, int sizeClass, Address availableHead) { boolean liveBlock = sweepCells(sweeper, block, sizeClass); if (!liveBlock) { BlockAllocator.setNext(block, Address.zero()); BlockAllocator.free(this, block); } else { BlockAllocator.setNext(block, availableHead); availableHead = block; } return availableHead; } /** * Sweep a block, freeing it and making it available if any live cells were found. * if it contains no free objects. * * This is designed to be called in parallel by multiple collector threads. */ public void parallelSweepCells(Sweeper sweeper) { for (int sizeClass = 0; sizeClass < sizeClassCount(); sizeClass++) { Address block; while(!(block = getSweepBlock(sizeClass)).isZero()) { boolean liveBlock = sweepCells(sweeper, block, sizeClass); if (!liveBlock) { BlockAllocator.setNext(block, Address.zero()); BlockAllocator.free(this, block); } else { lock.acquire(); BlockAllocator.setNext(block, availableBlockHead.get(sizeClass)); availableBlockHead.set(sizeClass, block); lock.release(); } } } } /** * Get a block for a parallel sweep. * * @param sizeClass The size class of the block to sweep. * @return The block or zero if no blocks remain to be swept. */ private Address getSweepBlock(int sizeClass) { lock.acquire(); Address block; /* Flushed blocks */ block = flushedBlockHead.get(sizeClass); if (!block.isZero()) { flushedBlockHead.set(sizeClass, BlockAllocator.getNext(block)); lock.release(); BlockAllocator.setNext(block, Address.zero()); return block; } /* Consumed blocks */ block = consumedBlockHead.get(sizeClass); if (!block.isZero()) { flushedBlockHead.set(sizeClass, BlockAllocator.getNext(block)); lock.release(); BlockAllocator.setNext(block, Address.zero()); return block; } /* All swept! */ lock.release(); return Address.zero(); } /** * Does this block contain any live cells? */ @Inline public boolean sweepCells(Sweeper sweeper, Address block, int sizeClass) { Extent blockSize = Extent.fromIntSignExtend(BlockAllocator.blockSize(blockSizeClass[sizeClass])); Address cursor = block.plus(blockHeaderSize[sizeClass]); Address end = block.plus(blockSize); Extent cellExtent = Extent.fromIntSignExtend(cellSize[sizeClass]); boolean containsLive = false; while (cursor.LT(end)) { ObjectReference current = VM.objectModel.getObjectFromStartAddress(cursor); boolean free = true; if (!current.isNull()) { free = !liveBitSet(current); if (!free) { free = sweeper.sweepCell(current); if (free) unsyncClearLiveBit(current); } } if (!free) { containsLive = true; } cursor = cursor.plus(cellExtent); } return containsLive; } /** * A callback used to perform sweeping of a free list space. */ @Uninterruptible public abstract static class Sweeper { public abstract boolean sweepCell(ObjectReference object); } /**************************************************************************** * * Live bit manipulation */ /** * Atomically set the live bit for a given object * * @param object The object whose live bit is to be set. * @return True if the bit was changed to true. */ @Inline public static boolean testAndSetLiveBit(ObjectReference object) { return updateLiveBit(VM.objectModel.objectStartRef(object), true, true); } /** * Set the live bit for the block containing the given object * * @param object The object whose blocks liveness is to be set. */ @Inline protected static void markBlock(ObjectReference object) { BlockAllocator.markBlockMeta(object); } /** * Set the live bit for the given block. * * @param block The block whose liveness is to be set. */ @Inline protected static void markBlock(Address block) { BlockAllocator.markBlockMeta(block); } /** * Set the live bit for a given object, without using * synchronization primitives---must only be used when contention * for live bit is strictly not possible * * @param object The object whose live bit is to be set. */ @Inline public static boolean unsyncSetLiveBit(ObjectReference object) { return updateLiveBit(VM.objectModel.refToAddress(object), true, false); } /** * Set the live bit for a given address * * @param address The address whose live bit is to be set. * @param set True if the bit is to be set, as opposed to cleared * @param atomic True if we want to perform this operation atomically */ @Inline private static boolean updateLiveBit(Address address, boolean set, boolean atomic) { Word oldValue, newValue; Address liveWord = getLiveWordAddress(address); Word mask = getMask(address, true); if (atomic) { do { oldValue = liveWord.prepareWord(); newValue = (set) ? oldValue.or(mask) : oldValue.and(mask.not()); } while (!liveWord.attempt(oldValue, newValue)); } else { oldValue = liveWord.loadWord(); liveWord.store(set ? oldValue.or(mask) : oldValue.and(mask.not())); } return oldValue.and(mask).NE(mask); } /** * Test the live bit for a given object * * @param object The object whose live bit is to be set. */ @Inline protected static boolean liveBitSet(ObjectReference object) { return liveBitSet(VM.objectModel.refToAddress(object)); } /** * Set the live bit for a given address * * @param address The address whose live bit is to be set. * @return true if this operation changed the state of the live bit. */ @Inline protected static boolean liveBitSet(Address address) { Address liveWord = getLiveWordAddress(address); Word mask = getMask(address, true); Word value = liveWord.loadWord(); return value.and(mask).EQ(mask); } /** * Clear the live bit for a given object * * @param object The object whose live bit is to be cleared. */ @Inline protected static void clearLiveBit(ObjectReference object) { clearLiveBit(VM.objectModel.refToAddress(object)); } /** * Clear the live bit for a given address * * @param address The address whose live bit is to be cleared. */ @Inline protected static void clearLiveBit(Address address) { updateLiveBit(address, false, true); } /** * Clear the live bit for a given object * * @param object The object whose live bit is to be cleared. */ @Inline protected static void unsyncClearLiveBit(ObjectReference object) { unsyncClearLiveBit(VM.objectModel.refToAddress(object)); } /** * Clear the live bit for a given address * * @param address The address whose live bit is to be cleared. */ @Inline protected static void unsyncClearLiveBit(Address address) { updateLiveBit(address, false, false); } /** * Clear all live bits for a block */ protected void clearLiveBits(Address block, int sizeClass) { int blockSize = BlockAllocator.blockSize(blockSizeClass[sizeClass]); Address cursor = getLiveWordAddress(block); Address sentinel = getLiveWordAddress(block.plus(blockSize - 1)); while (cursor.LE(sentinel)) { cursor.store(Word.zero()); cursor = cursor.plus(BYTES_IN_WORD); } } protected void zeroLiveBits() { Extent bytes = Extent.fromIntSignExtend(EmbeddedMetaData.BYTES_IN_REGION>>LOG_LIVE_COVERAGE); if (contiguous) { Address end = ((FreeListPageResource)pr).getHighWater(); Address cursor = start; while (cursor.LT(end)) { Address metadata = EmbeddedMetaData.getMetaDataBase(cursor).plus(META_DATA_OFFSET); VM.memory.zero(metadata, bytes); cursor = cursor.plus(EmbeddedMetaData.BYTES_IN_REGION); } } else { for(Address cursor = headDiscontiguousRegion; !cursor.isZero(); cursor = Map.getNextContiguousRegion(cursor)) { Address metadata = EmbeddedMetaData.getMetaDataBase(cursor).plus(META_DATA_OFFSET); VM.memory.zero(metadata, bytes); } } } /** * Align an address so that it corresponds to a live word boundary. * In other words, if the live bit for the given address is not the * zeroth bit of a live word, round the address down such that it * does. * * @param address The address to be aligned to a live word * @return The given address, aligned down so that it corresponds to * an address on a live word boundary. */ private static Address alignToLiveStride(Address address) { return address.toWord().and(LIVE_WORD_STRIDE_MASK).toAddress(); } /** * Given an address, produce a bit mask for the live table * * @param address The address whose live bit mask is to be established * @param set True if we want the mask for <i>setting</i> the bit, * false if we want the mask for <i>clearing</i> the bit. * @return The appropriate bit mask for object for the live table for. */ @Inline private static Word getMask(Address address, boolean set) { int shift = address.toWord().rshl(OBJECT_LIVE_SHIFT).and(WORD_SHIFT_MASK).toInt(); Word rtn = Word.one().lsh(shift); return (set) ? rtn : rtn.not(); } /** * Given an address, return the address of the live word for * that address. * * @param address The address whose live word address is to be returned * @return The address of the live word for this object */ @Inline private static Address getLiveWordAddress(Address address) { Address rtn = EmbeddedMetaData.getMetaDataBase(address); return rtn.plus(META_DATA_OFFSET).plus(EmbeddedMetaData.getMetaDataOffset(address, LOG_LIVE_COVERAGE, LOG_BYTES_IN_WORD)); } }