/* * Copyright (c) 2010, 2012, 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.heap.gcx.HeapRegionConstants.*; import static com.sun.max.vm.heap.gcx.HeapRegionInfo.Flag.*; import static com.sun.max.vm.heap.gcx.HeapRegionState.*; import com.sun.max.annotate.*; import com.sun.max.unsafe.*; import com.sun.max.vm.*; import com.sun.max.vm.heap.*; import com.sun.max.vm.reference.*; /** * Descriptor of a heap region. * The information recorded is carefully crafted so that a zero-filled HeapRegionInfo * instance represents the state of a free region, not allocated to any heap account. * This avoids initialization of the {@link RegionTable} entries at startup. */ public class HeapRegionInfo { static enum Flag { /** * Indicates that the region is part of an iterable range. * GCs and other operations walking over the heap must never come * across a non-iterable region. Empty regions are not iterable (they could be made iterable but * it is pointless to iterate over them). Allocating regions are considered non-iterable. */ IS_ITERABLE, /** * Indicates that the region is used by an allocator and may not be iterable. An allocation region must be made iterable before iterating over it. */ IS_ALLOCATING, /** * Region has a list of {@link HeapFreeChunk} tracking space available for allocation within the region. * The head of the list is located at the {@link HeapRegionInfo#firstFreeChunkOffset} word in the region. * Note that empty regions doesn't have free chunks according to this definition. */ HAS_FREE_CHUNK, /** * Region is part of a large multi-regions, object. */ IS_LARGE, /** * Region is the head of a large multi-regions object. */ IS_HEAD, /** * Region is the last region of a multi-regions object. Space after the end of the large object may be used for allocation. */ IS_TAIL; private final int mask = 1 << ordinal(); public final boolean isSet(int flags) { return (flags & mask) != 0; } public final boolean isClear(int flags) { return (flags & mask) == 0; } public final int or(int flags) { return flags | mask; } public final int and(int flags) { return flags & mask; } public final int xor(int flags) { return flags ^ mask; } public int clear(int flags) { return flags & ~mask; } public final int or(Flag flag) { return flag.mask | mask; } public final int and(Flag flag) { return flag.mask & mask; } public final boolean only(int flags) { return flags == mask; } static private Flag [] allFlags = values(); static void log(int flags) { String sep = ""; for (Flag f : allFlags) { if (f.isSet(flags)) { Log.print(sep); Log.print(f.toString()); sep = " | "; } } } } static final int LARGE_REGION_FLAGS = IS_LARGE.or(IS_TAIL.or(IS_HEAD.or(0))); public static String flagsToString(int flags) { if (flags == 0) { return "IS_EMPTY"; } StringBuffer b = new StringBuffer(""); String sep = ""; for (Flag f : Flag.values()) { if (f.isSet(flags)) { b.append(sep); b.append(f); sep = " | "; } } return b.toString(); } /** * A 32-bit vector compounding several flags information. See {@link Flag} for usage of each of the bits. */ @INSPECTED int flags; // NOTE: don't want to use an EnumSet here. Don't want a long for storing flags; and want the flags embedded in the heap region info. /** * A customizable tag used to discriminate regions. */ @INSPECTED int tag; /** * Offset (in bytes) relative to the beginning of a region to the first free chunk of the region. * Zero if the region is empty. */ @INSPECTED private int firstFreeChunkOffset; /** * Number of free chunks. Zero if the region is empty. */ @INSPECTED private int numFreeChunks; /** * Space available for allocation, in bytes. This excludes dark matter than cannot be used * for allocation. Zero if the region is empty. */ private int freeSpace; /** * Amount of live data, in bytes. Zero if the region is empty. * Can be used with {@link #freeSpace} to determine dark matter. */ private int liveData; /** * Owner of the region described by {@link HeapRegionInfo} instance. */ HeapAccountOwner owner; public final boolean isEmpty() { return flags == EMPTY_REGION.flags; } public final boolean isFull() { return IS_ITERABLE.only(flags & ~LARGE_REGION_FLAGS); } public final boolean isAllocating() { return IS_ALLOCATING.isSet(flags); } public final boolean isIterable() { return IS_ITERABLE.isSet(flags); } public final boolean hasFreeChunks() { return HAS_FREE_CHUNK.isSet(flags); } public final boolean isLarge() { return IS_LARGE.isSet(flags); } public final boolean isHeadOfLargeObject() { return IS_HEAD.isSet(flags); } public final boolean isTailOfLargeObject() { return IS_TAIL.isSet(flags); } HeapRegionInfo() { // Not a class one can allocate. Allocation is the responsibility of the region table. } /** * Total number of free bytes in free chunks. This is only relevant for region with at least one free chunk. * Empty regions have a free bytes count of zero. */ public final int freeBytesInChunks() { return freeSpace; } public final int freeBytes() { return isEmpty() ? regionSizeInBytes : freeBytesInChunks(); } public final int liveBytes() { return liveData << Word.widthValue().log2numberOfBytes; } public final int numFreeChunks() { return numFreeChunks; } public void dump(boolean enumerateFreeChunks) { Log.print("region #"); Log.print(toRegionID()); Log.print(" ["); Log.print(regionStart()); Log.print(","); Log.print(regionStart().plus(regionSizeInBytes)); Log.print(" [ "); Flag.log(flags); Log.print(", free: "); Log.print(freeBytes()); Log.print(" live: "); Log.print(liveBytes()); Log.print(" owner: "); Log.print(Reference.fromJava(owner).toOrigin()); Log.print(" #free chunks: "); Log.print(numFreeChunks); if (numFreeChunks > 0) { if (enumerateFreeChunks) { Log.print("free chunks: "); HeapFreeChunk.dumpList(HeapFreeChunk.toHeapFreeChunk(firstFreeBytes())); } else { Log.print("first free chunk"); Log.print(firstFreeBytes()); } } Log.println(); } /** * Return the address to the first chunk of space available for allocation. * The chunk is formatted as a {@link HeapFreeChunk} only if the region has its {@link Flag#HAS_FREE_CHUNK} set. * @return Address to the first chunk of space available for allocation, or zero if the region is full. */ final Address firstFreeBytes() { return isFull() ? Address.zero() : regionStart().plus(firstFreeChunkOffset); } private int offsetInRegion(Address address) { return address.and(regionAlignmentMask).toInt(); } /** * Start of the region described by this {@link HeapRegionInfo} instance. * @return address to the start of a region */ final Address regionStart() { return RegionTable.theRegionTable().regionAddress(this); } private void clear() { liveData = 0; numFreeChunks = 0; firstFreeChunkOffset = 0; freeSpace = 0; } final void setFreeChunks(Address firstChunkAddress, int numBytes, int numChunks) { firstFreeChunkOffset = offsetInRegion(firstChunkAddress); numFreeChunks = numChunks; freeSpace = numBytes; } final void setFreeChunks(Address firstChunkAddress, Size numBytes, int numChunks) { setFreeChunks(firstChunkAddress, numBytes.toInt(), numChunks); } final void clearFreeChunks() { firstFreeChunkOffset = 0; numFreeChunks = 0; freeSpace = 0; } final void resetOccupancy() { clear(); } public final HeapAccountOwner owner() { return owner; } final void setOwner(HeapAccountOwner owner) { this.owner = owner; } final void setTag(int tag) { this.tag = tag; } final int getTag() { return tag; } final HeapRegionInfo next() { return RegionTable.theRegionTable().next(this); } final HeapRegionInfo prev() { return RegionTable.theRegionTable().prev(this); } final int toRegionID() { return RegionTable.theRegionTable().regionID(this); } @INLINE static HeapRegionInfo fromRegionID(int regionID) { return RegionTable.theRegionTable().regionInfo(regionID); } /** * Return heap region associated information from an address guaranteed to point in a heap region. * @param address */ @INLINE static HeapRegionInfo fromInRegionAddress(Address address) { return RegionTable.theRegionTable().inHeapAddressRegionInfo(address); } /** * Return heap region associated information from an address not guaranteed to point in a heap region. * Return {@linkplain RegionTable#nullHeapRegionInfo} if not. * @param address */ @INLINE static HeapRegionInfo fromAddress(Address address) { return RegionTable.theRegionTable().regionInfo(address); } @INLINE static void walk(RegionRange regionRange, CellVisitor cellVisitor) { RegionTable.theRegionTable().walk(regionRange, cellVisitor); } }