/* * 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 static org.mmtk.utility.Constants.*; import static org.mmtk.utility.heap.layout.HeapParameters.MAX_SPACES; import static org.mmtk.utility.heap.layout.VMLayoutConstants.*; import org.mmtk.plan.Plan; import org.mmtk.plan.TransitiveClosure; import org.mmtk.utility.heap.PageResource; import org.mmtk.utility.heap.SpaceDescriptor; import org.mmtk.utility.heap.VMRequest; import org.mmtk.utility.heap.layout.HeapLayout; import org.mmtk.utility.options.Options; import org.mmtk.utility.Conversions; import org.mmtk.utility.Log; import org.mmtk.vm.VM; import org.vmmagic.pragma.*; import org.vmmagic.unboxed.*; /** * This class defines and manages spaces. Each policy is an instance * of a space. A space is a region of virtual memory (contiguous or * discontigous) which is subject to the same memory management * regime. Multiple spaces (instances of this class or its * descendants) may have the same policy (eg there could be numerous * instances of CopySpace, each with different roles). Spaces are * defined in terms of a unique region of virtual memory, so no two * space instances ever share any virtual memory.<p> * * In addition to tracking virtual memory use and the mapping to * policy, spaces also manage memory consumption (<i>used</i> virtual * memory). * */ @Uninterruptible public abstract class Space { /**************************************************************************** * * Class variables */ /** * */ private static boolean DEBUG = false; private static final boolean FORCE_SLOW_MAP_LOOKUP = false; private static final int PAGES = 0; private static final int MB = 1; private static final int PAGES_MB = 2; private static final int MB_PAGES = 3; private static int spaceCount = 0; private static Space[] spaces = new Space[MAX_SPACES]; private static Address heapCursor = HEAP_START; private static Address heapLimit = HEAP_END; /**************************************************************************** * * Instance variables */ /** * */ private final String name; private final int nameLength; protected final int descriptor; private final int index; private final VMRequest vmRequest; protected final boolean immortal; protected final boolean movable; protected final boolean contiguous; protected final boolean zeroed; protected PageResource pr; protected final Address start; protected final Extent extent; protected Address headDiscontiguousRegion; /**************************************************************************** * * Initialization */ { if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(PAGES_IN_CHUNK > 1); } /** * This is the base constructor for <i>all</i> spaces.<p> * * @param name The name of this space (used when printing error messages etc) * @param movable Are objects in this space movable? * @param immortal Are objects in this space immortal (uncollected)? * @param zeroed if it is {@code true}, allocated memory is zeroed. * @param vmRequest An object describing the virtual memory requested. */ protected Space(String name, boolean movable, boolean immortal, boolean zeroed, VMRequest vmRequest) { this.name = name; this.nameLength = name.length(); // necessary to avoid calling length() in uninterruptible code this.movable = movable; this.immortal = immortal; this.zeroed = zeroed; this.vmRequest = vmRequest; this.index = spaceCount++; spaces[index] = this; this.headDiscontiguousRegion = Address.zero(); if (vmRequest.type == VMRequest.REQUEST_DISCONTIGUOUS) { this.contiguous = false; this.descriptor = SpaceDescriptor.createDescriptor(); this.start = Address.zero(); this.extent = Extent.zero(); VM.memory.setHeapRange(index, HEAP_START, HEAP_END); // this should really be refined! Once we have a code space, we can be a lot more specific about what is a valid code heap area return; } Address start; Extent extent; if (vmRequest.type == VMRequest.REQUEST_FRACTION) { extent = getFracAvailable(vmRequest.frac); } else { extent = vmRequest.extent; } if (extent.NE(Conversions.chunkAlign(extent, false))) { VM.assertions.fail(name + " requested non-aligned extent: " + extent.toLong() + " bytes"); } if (vmRequest.type == VMRequest.REQUEST_FIXED) { start = vmRequest.start; if (start.NE(Conversions.chunkAlign(start, false))) { VM.assertions.fail(name + " starting on non-aligned boundary: " + start.toLong() + " bytes"); } } else if (vmRequest.top) { if (HeapLayout.vmMap.isFinalized()) VM.assertions.fail("heap is narrowed after regionMap is finalized: " + name); heapLimit = heapLimit.minus(extent); start = heapLimit; } else { start = heapCursor; heapCursor = heapCursor.plus(extent); } if (heapCursor.GT(heapLimit)) { Log.write("Out of virtual address space allocating \""); Log.write(name); Log.write("\" at ", heapCursor.minus(extent)); Log.write(" (", heapCursor); Log.write(" > ", heapLimit); Log.writeln(")"); VM.assertions.fail("exiting"); } this.contiguous = true; this.start = start; this.extent = extent; this.descriptor = SpaceDescriptor.createDescriptor(start, start.plus(extent)); VM.memory.setHeapRange(index, start, start.plus(extent)); HeapLayout.vmMap.insert(start, extent, descriptor, this); if (DEBUG) { Log.write(name); Log.write(" ", start); Log.write(" ", start.plus(extent)); Log.writeln(" ", extent.toWord()); } } /**************************************************************************** * * Accessor methods */ /** @return The start of the discontiguous space */ public static Address getDiscontigStart() { return heapCursor; } /** @return The end of the discontiguous space */ public static Address getDiscontigEnd() { return heapLimit.minus(1); } /** @return The name of this space */ public final String getName() { return name; } /** @return The start address of this space */ public final Address getStart() { return start; } /** @return The size (extent) of this space */ public final Extent getExtent() { return extent; } /** @return The integer descriptor for this space */ public final int getDescriptor() { return descriptor; } /** @return The index (ordinal number) of this space */ public final int getIndex() { return index; } /** @return {@code true} if this space is never collected */ public final boolean isImmortal() { return immortal; } /** @return {@code true} if objects in this space may move */ public boolean isMovable() { return movable; } /** @return The number of reserved pages */ public final int reservedPages() { return pr.reservedPages(); } /** @return The number of committed pages */ public final int committedPages() { return pr.committedPages(); } /** @return The number of pages available for allocation */ public final int availablePhysicalPages() { return pr.getAvailablePhysicalPages(); } /** @return Cumulative committed pages. */ public static long cumulativeCommittedPages() { return PageResource.cumulativeCommittedPages(); } /**************************************************************************** * * Object and address tests / accessors */ /** * Return {@code true} if the given object is in an immortal (uncollected) space. * * @param object The object in question * @return {@code true} if the given object is in an immortal (uncollected) space. */ public static boolean isImmortal(ObjectReference object) { Space space = getSpaceForObject(object); if (space == null) return true; else return space.isImmortal(); } /** * Return {@code true} if the given object is in space that moves objects. * * @param object The object in question * @return {@code true} if the given object is in space that moves objects. */ @Inline public static boolean isMovable(ObjectReference object) { Space space = getSpaceForObject(object); if (space == null) return true; else return space.isMovable(); } /** * Return {@code true} if the given object is in a space managed by MMTk. * * @param object The object in question * @return {@code true} if the given object is in a space managed by MMTk. */ @Inline public static boolean isMappedObject(ObjectReference object) { return !object.isNull() && (getSpaceForObject(object) != null) && HeapLayout.mmapper.objectIsMapped(object); } /** * Return {@code true} if the given address is in a space managed by MMTk. * * @param address The address in question * @return {@code true} if the given address is in a space managed by MMTk. */ @Inline public static boolean isMappedAddress(Address address) { return HeapLayout.vmMap.getSpaceForAddress(address) != null && HeapLayout.mmapper.addressIsMapped(address); } /** * Return {@code true} if the given object is the space associated with the * given descriptor. * * @param descriptor The descriptor for a space * @param object The object in question * @return {@code true} if the given object is in the space associated with * the descriptor. */ @Inline public static boolean isInSpace(int descriptor, ObjectReference object) { if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(!object.isNull()); return isInSpace(descriptor, VM.objectModel.refToAddress(object)); } /** * Return {@code true} if the given address is the space associated with the * given descriptor. * * @param descriptor The descriptor for a space * @param address The address in question. * @return {@code true} if the given address is in the space associated with * the descriptor. */ @Inline public static boolean isInSpace(int descriptor, Address address) { if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(!address.isZero()); if (FORCE_SLOW_MAP_LOOKUP || !SpaceDescriptor.isContiguous(descriptor)) { return HeapLayout.vmMap.getDescriptorForAddress(address) == descriptor; } else { Address start = SpaceDescriptor.getStart(descriptor); if (!VM.VERIFY_ASSERTIONS && SpaceDescriptor.isContiguousHi(descriptor)) return address.GE(start); else { Extent size = SpaceDescriptor.getExtent(descriptor); Address end = start.plus(size); return address.GE(start) && address.LT(end); } } } /** * Return the space for a given object * * @param object The object in question * @return The space containing the object */ @Inline public static Space getSpaceForObject(ObjectReference object) { if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(!object.isNull()); return HeapLayout.vmMap.getSpaceForAddress(VM.objectModel.refToAddress(object)); } /** * Return the space for a given address, not necessarily the * start address of an object. * * @param addr The address in question * @return The space containing the address */ public static Space getSpaceForAddress(Address addr) { if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(!addr.isZero()); return HeapLayout.vmMap.getSpaceForAddress(addr); } /**************************************************************************** * * Page management */ /** * Updates the zeroing approach for this space. * * @param useNT whether to use non-temporal instructions for zeroing * @param concurrent whether zeroing will be done concurrently */ @Interruptible public void setZeroingApproach(boolean useNT, boolean concurrent) { pr.updateZeroingApproach(useNT, concurrent); } /** * Skip concurrent zeroing (fall back to bulk zeroing). */ public void skipConcurrentZeroing() { pr.skipConcurrentZeroing(); } /** * Trigger concurrent zeroing. */ public void triggerConcurrentZeroing() { pr.triggerConcurrentZeroing(); } /** * Acquire a number of pages from the page resource, returning * either the address of the first page, or zero on failure.<p> * * This may trigger a GC if necessary.<p> * * First the page budget is checked to see whether polling the GC is * necessary. If so, the GC is polled. If a GC is required then the * request fails and zero is returned.<p> * * If the check of the page budget does not lead to GC being * triggered, then a request is made for specific pages in virtual * memory. If the page manager cannot satisify this request, then * the request fails, a GC is forced, and zero is returned. * Otherwise the address of the first page is returned.<p> * * @param pages The number of pages requested * @return The start of the first page if successful, zero on * failure. */ @LogicallyUninterruptible public final Address acquire(int pages) { boolean allowPoll = VM.activePlan.isMutator() && Plan.isInitialized(); /* Check page budget */ int pagesReserved = pr.reservePages(pages); /* Poll, either fixing budget or requiring GC */ if (allowPoll && VM.activePlan.global().poll(false, this)) { pr.clearRequest(pagesReserved); VM.collection.blockForGC(); return Address.zero(); // GC required, return failure } /* Page budget is ok, try to acquire virtual memory */ Address rtn = pr.getNewPages(pagesReserved, pages, zeroed); if (rtn.isZero()) { /* Failed, so force a GC */ if (!allowPoll) VM.assertions.fail("Physical allocation failed when polling not allowed!"); boolean gcPerformed = VM.activePlan.global().poll(true, this); if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(gcPerformed, "GC not performed when forced."); pr.clearRequest(pagesReserved); VM.collection.blockForGC(); return Address.zero(); } return rtn; } /** * Extend the virtual memory associated with a particular discontiguous * space. This simply involves requesting a suitable number of chunks * from the pool of chunks available to discontiguous spaces. * * @param chunks The number of chunks by which the space needs to be extended * @return The address of the new discontiguous space. */ public Address growDiscontiguousSpace(int chunks) { Address newHead = HeapLayout.vmMap.allocateContiguousChunks(descriptor, this, chunks, headDiscontiguousRegion); if (newHead.isZero()) { return Address.zero(); } return headDiscontiguousRegion = newHead; } /** * Return the number of chunks required to satisfy a request for a certain number of pages * * @param pages The number of pages desired * @return The number of chunks needed to satisfy the request */ public static int requiredChunks(int pages) { Extent extent = Conversions.chunkAlign(Conversions.pagesToBytes(pages), false); return extent.toWord().rshl(LOG_BYTES_IN_CHUNK).toInt(); } /** * This hook is called by page resources each time a space grows. The space may * tap into the hook to monitor heap growth. The call is made from within the * page resources' critical region, immediately before yielding the lock. * * @param start The start of the newly allocated space * @param bytes The size of the newly allocated space * @param newChunk {@code true} if the new space encroached upon or started a new chunk or chunks. */ public void growSpace(Address start, Extent bytes, boolean newChunk) {} /** * Release one or more contiguous chunks associated with a discontiguous * space. * * @param chunk The address of the start of the contiguous chunk or chunks * @return The number of chunks freed */ public int releaseDiscontiguousChunks(Address chunk) { if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(chunk.EQ(Conversions.chunkAlign(chunk, true))); if (chunk.EQ(headDiscontiguousRegion)) { headDiscontiguousRegion = HeapLayout.vmMap.getNextContiguousRegion(chunk); } return HeapLayout.vmMap.freeContiguousChunks(chunk); } /** * @return The address of the head of the discontiguous chunk map. */ public Address getHeadDiscontiguousRegion() { return headDiscontiguousRegion; } public void releaseAllChunks() { HeapLayout.vmMap.freeAllChunks(headDiscontiguousRegion); headDiscontiguousRegion = Address.zero(); } /** * Release a unit of allocation (a page or pages) * * @param start The address of the start of the region to be released */ public abstract void release(Address start); /** * Get the total number of pages reserved by all of the spaces * * @return the total number of pages reserved by all of the spaces */ private static int getPagesReserved() { int pages = 0; for (int i = 0; i < spaceCount; i++) { pages += spaces[i].reservedPages(); } return pages; } /**************************************************************************** * * Debugging / printing */ /** * Print out the memory used by all spaces, in megabytes */ public static void printUsageMB() { printUsage(MB); } /** * Print out the memory used by all spaces, in megabytes */ public static void printUsagePages() { printUsage(PAGES); } /** * Print out a map of virtual memory usage by all spaces */ public static void printVMMap() { Log.writeln("Key: (I)mmortal (N)onmoving (D)iscontiguous (E)xtent (F)raction"); Log.writeln(" HEAP_START ", HEAP_START); Log.writeln("AVAILABLE_START ", AVAILABLE_START); for (int i = 0; i < spaceCount; i++) { Space space = spaces[i]; for (int s = 0; s < 11 - space.nameLength; s++) Log.write(" "); Log.write(space.name); Log.write(" "); Log.write(space.immortal ? "I" : " "); Log.write(space.movable ? " " : "N"); if (space.contiguous) { Log.write(" ", space.start); Log.write("->", space.start.plus(space.extent.minus(1))); if (space.vmRequest.type == VMRequest.REQUEST_EXTENT) { Log.write(" E ", space.vmRequest.extent); } else if (space.vmRequest.type == VMRequest.REQUEST_FRACTION) { Log.write(" F "); Log.write(space.vmRequest.frac); } Log.writeln(); } else { Log.write("D ["); for (Address a = space.headDiscontiguousRegion; !a.isZero(); a = HeapLayout.vmMap.getNextContiguousRegion(a)) { Log.write(a); Log.write("->"); Log.write(a.plus(HeapLayout.vmMap.getContiguousRegionSize(a).minus(1))); if (!HeapLayout.vmMap.getNextContiguousRegion(a).isZero()) Log.write(", "); } Log.writeln("]"); } } Log.writeln(" AVAILABLE_END ", AVAILABLE_END); Log.writeln(" HEAP_END ", HEAP_END); } /** * Interface to use to implement the Visitor Pattern for Spaces. */ public interface SpaceVisitor { void visit(Space s); } /** * Implement the Visitor Pattern for Spaces. * @param v The visitor to perform on each Space instance */ @Interruptible public static void visitSpaces(SpaceVisitor v) { for (int i = 0; i < spaceCount; i++) { v.visit(spaces[i]); } } /** * Ensure that all MMTk spaces (all spaces aside from the VM space) * are mapped. Demand zero map all of them if they are not already * mapped. */ @Interruptible public static void eagerlyMmapMMTkSpaces() { eagerlyMmapMMTkContiguousSpaces(); eagerlyMmapMMTkDiscontiguousSpaces(); } /** * Ensure that all contiguous MMTk spaces are mapped. Demand zero map * all of them if they are not already mapped. */ @Interruptible public static void eagerlyMmapMMTkContiguousSpaces() { for (int i = 0; i < spaceCount; i++) { Space space = spaces[i]; if (space != VM.memory.getVMSpace()) { if (Options.verbose.getValue() > 2) { Log.write("Mapping "); Log.write(space.name); Log.write(" "); Log.write(space.start); Log.write("->"); Log.writeln(space.start.plus(space.extent.minus(1))); } HeapLayout.mmapper.ensureMapped(space.start, space.extent.toInt() >> LOG_BYTES_IN_PAGE); } } } /** * Ensure that all discontiguous MMTk spaces are mapped. Demand zero map * all of them if they are not already mapped. */ @Interruptible public static void eagerlyMmapMMTkDiscontiguousSpaces() { Address regionStart = Space.getDiscontigStart(); Address regionEnd = Space.getDiscontigEnd(); int pages = regionEnd.diff(regionStart).toInt() >> LOG_BYTES_IN_PAGE; if (Options.verbose.getValue() > 2) { Log.write("Mapping discontiguous spaces "); Log.write(regionStart); Log.write("->"); Log.writeln(regionEnd.minus(1)); } HeapLayout.mmapper.ensureMapped(getDiscontigStart(), pages); } /** * Print out the memory used by all spaces in either megabytes or * pages. * * @param mode An enumeration type that specifies the format for the * prining (PAGES, MB, PAGES_MB, or MB_PAGES). */ private static void printUsage(int mode) { Log.write("used = "); printPages(getPagesReserved(), mode); boolean first = true; for (int i = 0; i < spaceCount; i++) { Space space = spaces[i]; Log.write(first ? " = " : " + "); first = false; Log.write(space.name); Log.write(" "); printPages(space.reservedPages(), mode); } Log.writeln(); } /** * Print out the number of pages and or megabytes, depending on the mode. * * @param pages The number of pages * @param mode An enumeration type that specifies the format for the * printing (PAGES, MB, PAGES_MB, or MB_PAGES). */ private static void printPages(int pages, int mode) { double mb = (double) (pages << LOG_BYTES_IN_PAGE) / (double) (1 << 20); switch (mode) { case PAGES: Log.write(pages); Log.write(" pgs"); break; case MB: Log.write(mb); Log.write(" Mb"); break; case PAGES_MB: Log.write(pages); Log.write(" pgs ("); Log.write(mb); Log.write(" Mb)"); break; case MB_PAGES: Log.write(mb); Log.write(" Mb ("); Log.write(pages); Log.write(" pgs)"); break; default: VM.assertions.fail("writePages passed illegal printing mode"); } } /**************************************************************************** * * Miscellaneous */ /** * Trace an object as part of a collection and return the object, * which may have been forwarded (if a copying collector). * * @param trace The trace being conducted. * @param object The object to trace * @return The object, forwarded, if appropriate */ public abstract ObjectReference traceObject(TransitiveClosure trace, ObjectReference object); /** * Has the object in this space been reached during the current collection. * This is used for GC Tracing. * * @param object The object reference. * @return {@code true} if the object is reachable. */ public boolean isReachable(ObjectReference object) { return isLive(object); } /** * Is the object in this space alive? * * @param object The object reference. * @return {@code true} if the object is live. */ public abstract boolean isLive(ObjectReference object); /** * Convert a fraction into a number of bytes according to the * fraction of available bytes. * * @param frac The fraction of available virtual memory desired * @return The corresponding number of bytes, chunk-aligned. */ public static Extent getFracAvailable(float frac) { long bytes = (long) (frac * AVAILABLE_BYTES.toLong()); Word mb = Word.fromIntSignExtend((int) (bytes >> LOG_BYTES_IN_MBYTE)); Extent rtn = mb.lsh(LOG_BYTES_IN_MBYTE).toExtent(); return Conversions.chunkAlign(rtn, false); } /** @return the actual number of spaces in the space array */ public static int getSpaceCount() { return spaceCount; } /** * @return the spaces array. Note that the array is partially empty: * use {@link #getSpaceCount()} to determine the maximum index that * is still filled. */ public static Space[] getSpaces() { return spaces; } }