/* * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.max.vm.heap.gcx; import static com.sun.max.vm.VMOptions.*; import com.sun.max.lang.*; import com.sun.max.unsafe.*; import com.sun.max.vm.*; import com.sun.max.vm.heap.*; import com.sun.max.vm.object.*; import com.sun.max.vm.runtime.*; /** * A rescan map keeps track of the areas that hold grey objects after a marking stack overflow. * It helps limiting rescan of the mark bitmaps. * The rescan map logically decomposes the mark bitmap associated to the heap in fixed sized region and for each region, tracks * the interval comprising grey marks. * The rescan map is updated when the marking stack overflow, and used during overflow rescan. */ public class RescanMap { private static short shortZero = (short) 0; /** * VM option to set the size of a rescan map entry. It is expressed in terms * of mark-bitmap storage. The VM rounds this up to the nearest power of 2 size corresponding * to a integral number of mark bitmap words. * * Assuming each mark bit covers 8-byte heap words, a region size of 1Kb covers 64 Kb of heap. * */ static VMSizeOption rescanRegionSizeOption = register(new VMSizeOption("-XX:RescanRegionSize=", Size.K.times(1), "Unit of rescan after marking stack overflow"), MaxineVM.Phase.PRISTINE); /** * Address to the first byte of the heap covered by the mark bitmap. */ private Address coveredAreaStart; /** * Rescan region size, in number of mark bitmap bytes. Must be a power of 2. */ private int rescanRegionSize; /** * The log2 of the number of words per rescan map regions. */ private int log2NumWordsPerRescanRegion; private int log2BitmapWord; /** * The log2 to shift a heap offset to obtain an index to the entry of the rescan map. */ private int log2AddressToRescanMapIndex; /** * Mask to obtain the offset in the rescan region from a heap address. */ private int rescanRegionOffsetMask; /** * The table tracking intervals of mark bitmap holdings grey marks. * There are two entries per logical region of the mark bitmap, for * respectively the first and last grey marks within that region. * A short index allows to identify 2^16 mark word, each holding 2^3 mark bits. Assuming a coverage of 8 bytes per * mark, this allows for logical regions of up to 2^25 = 32 Mb of heap. * A rescan region is typically much smaller than that (in the order of a few dozen of Kb). * * An entry records a range of mark bitmap words to scan. Because we want grey-free areas to be recorded with * 0, we use the convention of recording an interval [a,b] as <a, b+1>, i.e., the range [0,0[ indicates * that the corresponding mark bitmap regions doesn't have any grey objects. */ private int[] rescanMapTable; /** * Index to the leftmost entry of the rescan map that contains grey objects. */ private int leftmostEntry; /** * Index to the rightmost entry of the rescan map that contains grey objects. */ private int rightmostEntry; /** * Index to the leftmost mark word with a grey bit in the leftmost rescan map entry. */ private int leftmostLeftBound; /** * Index to the rightmost mark word with a grey bit in the leftmost rescan map entry. */ private int leftmostRightBound; private int cachedRescanMapEntry; private static final int shortMask = (1 << WordWidth.BITS_16.numberOfBits) - 1; private int rightBound(int rescanMapEntry) { return rescanMapEntry >> WordWidth.BITS_16.numberOfBits; } private int leftBound(int rescanMapEntry) { return rescanMapEntry & shortMask; } private int rescanMapEntry(int leftBound, int rightBound) { return leftBound | (rightBound << WordWidth.BITS_16.numberOfBits); } /** * Cache the bounds of the leftmost entry in the rescan map. * Used in with {@link #fetchNextEntry()} to iterate over a mark-bitmap covered by the rescan map. * The bounds are indexes (in number of words) to the mark bitmap. */ void cacheLeftmostEntryBound() { final int baseOfLeftmostEntry = leftmostEntry << log2NumWordsPerRescanRegion; cachedRescanMapEntry = ArrayAccess.getInt(rescanMapTable, leftmostEntry); leftmostLeftBound = baseOfLeftmostEntry + leftBound(cachedRescanMapEntry); leftmostRightBound = baseOfLeftmostEntry + rightBound(cachedRescanMapEntry) - 1; if (MaxineVM.isDebug()) { FatalError.check(leftmostLeftBound <= leftmostRightBound, "Invalid rescan map entry"); } } // DEBUG ONLY -- REMOVE Address beginOfGreyArea() { final int baseOfLeftmostEntry = leftmostEntry << log2NumWordsPerRescanRegion; final int rescanMapEntry = ArrayAccess.getInt(rescanMapTable, leftmostEntry); final int bitmapWordIndex = baseOfLeftmostEntry + leftBound(rescanMapEntry); return coveredAreaStart.plus(bitmapWordIndex << log2BitmapWord); } /** * Returns the index to the word of the mark-bitmap corresponding to * the leftmost mark-bitmap word of the leftmost rescan region recorded * in the rescan map associated to the mark bitmap. * @see #cacheLeftmostEntryBound() * * @return index to a word of the mark bitmap covered by the rescan map * */ int leftmostLeftBound() { return leftmostLeftBound; } /** * Returns the index to the word of the mark-bitmap corresponding to * the rightmost mark-bitmap word of the leftmost rescan region recorded in * the rescan map associated to the mark bitmap. * @see #cacheLeftmostEntryBound() * * @return index to a word of the mark bitmap covered by the rescan map * */ int leftmostRightBound() { return leftmostRightBound; } /** * Clear the leftmost entry in the rescan map and search the rescan map for next entry holding grey marks, * and set cursor to the next leftmost entry in the rescan map. */ void fetchNextEntry() { final int currentEntry = ArrayAccess.getInt(rescanMapTable, leftmostEntry); if (currentEntry != cachedRescanMapEntry) { if (MaxineVM.isDebug()) { FatalError.check(leftmostLeftBound == leftBound(currentEntry), "Left bound of updated rescan map entry must not have changed"); FatalError.check(leftmostRightBound < rightBound(currentEntry), "Right bound of updated rescan map entry must be greater"); } // Set left bound of the rescan map to the previous right bound to avoid rescanning whole entry. Just want to scan // the added part. ArrayAccess.setInt(rescanMapTable, leftmostEntry, rescanMapEntry(leftmostRightBound, rightBound(currentEntry))); return; } // Clear the leftmost entry: ArrayAccess.setInt(rescanMapTable, leftmostEntry++, 0); while (leftmostEntry <= rightmostEntry) { if (ArrayAccess.getInt(rescanMapTable, leftmostEntry) != 0) { return; } leftmostEntry++; } // Rescan map empty, clear its bounds. resetRescanBound(); } /** * Return a boolean indicating whether the rescan map is empty, i.e., has no recorded words of the mark bitmap containing grey objects. * @return true if the rescan map is empty, false otherwise. */ boolean isEmpty() { return leftmostEntry == rescanMapTable.length; } RescanMap() { } void resetRescanBound() { leftmostEntry = rescanMapTable.length; rightmostEntry = 0; } // FOR DEBUGGING. REMOVE ME TricolorHeapMarker tricolorHeapMarker; void recordCellForRescan(Pointer cell) { final int bitmapWordIndex = cell.minus(coveredAreaStart).unsignedShiftedRight(log2BitmapWord).toInt(); final int rescanMapIndex = bitmapWordIndex >> log2NumWordsPerRescanRegion; final int indexInRescanRegion = bitmapWordIndex & rescanRegionOffsetMask; if (MaxineVM.isDebug()) { final int index = (rescanMapIndex << log2NumWordsPerRescanRegion) + indexInRescanRegion; FatalError.check(index == tricolorHeapMarker.bitmapWordIndex(cell), "Bitmap Word Index from rescan map is incorrect"); final boolean lockDisabledSafepoints = Log.lock(); Log.print("Recording cell "); Log.print(cell); Log.print(" bit index: "); Log.print(cell.minus(coveredAreaStart).unsignedShiftedRight(tricolorHeapMarker.log2BytesCoveredPerBit).toInt()); Log.print(" bitmap word:"); Log.print(bitmapWordIndex); Log.print(" rescan map index: "); Log.print(rescanMapIndex); Log.print(" index in rescan region: "); Log.println(indexInRescanRegion); Log.unlock(lockDisabledSafepoints); } if (rightmostEntry < rescanMapIndex) { rightmostEntry = rescanMapIndex; } if (leftmostEntry > rescanMapIndex) { leftmostEntry = rescanMapIndex; ArrayAccess.setInt(rescanMapTable, rescanMapIndex, rescanMapEntry(indexInRescanRegion, indexInRescanRegion + 1)); return; } final int currentEntry = ArrayAccess.getInt(rescanMapTable, rescanMapIndex); final int leftBound = leftBound(currentEntry); if (leftBound > indexInRescanRegion) { ArrayAccess.setInt(rescanMapTable, rescanMapIndex, rescanMapEntry(indexInRescanRegion, rightBound(currentEntry))); } else if (indexInRescanRegion >= rightBound(currentEntry)) { ArrayAccess.setInt(rescanMapTable, rescanMapIndex, rescanMapEntry(leftBound, indexInRescanRegion + 1)); } } /** * Initialize the rescan map of a tricolor heap marker. * @param tricolorHeapMarker the tricolor heap marker associated with the rescan map */ void initialize(TricolorHeapMarker tricolorHeapMarker) { coveredAreaStart = tricolorHeapMarker.coveredAreaStart; rescanRegionSize = rescanRegionSizeOption.getValue().alignUp(1 << tricolorHeapMarker.log2BytesCoveredPerBit).toInt(); final int log2RescanRegionSize = Integer.numberOfTrailingZeros(rescanRegionSize); log2NumWordsPerRescanRegion = log2RescanRegionSize - WordWidth.BITS_64.log2numberOfBytes; log2BitmapWord = tricolorHeapMarker.log2BitmapWord; Size rescanMapSize = tricolorHeapMarker.colorMap.size().unsignedShiftedRight(log2RescanRegionSize); rescanRegionOffsetMask = (1 << log2NumWordsPerRescanRegion) - 1; Heap.enableImmortalMemoryAllocation(); rescanMapTable = new int[rescanMapSize.toInt()]; Heap.disableImmortalMemoryAllocation(); resetRescanBound(); FatalError.check(rescanRegionSize == 1 << log2RescanRegionSize, "RescanMap region size must be a power of 2"); if (MaxineVM.isDebug()) { this.tricolorHeapMarker = tricolorHeapMarker; } } }