| /* |
| * 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.plan; |
| |
| import org.mmtk.policy.MarkSweepSpace; |
| import org.mmtk.policy.Space; |
| import org.mmtk.policy.ImmortalSpace; |
| import org.mmtk.policy.RawPageSpace; |
| import org.mmtk.policy.LargeObjectSpace; |
| import org.mmtk.utility.alloc.LinearScan; |
| import org.mmtk.utility.Constants; |
| import org.mmtk.utility.Conversions; |
| import org.mmtk.utility.heap.HeapGrowthManager; |
| import org.mmtk.utility.heap.Map; |
| import org.mmtk.utility.heap.VMRequest; |
| import org.mmtk.utility.Log; |
| import org.mmtk.utility.options.*; |
| import org.mmtk.utility.sanitychecker.SanityChecker; |
| import org.mmtk.utility.statistics.Timer; |
| import org.mmtk.utility.statistics.Stats; |
| |
| import org.mmtk.vm.VM; |
| import org.mmtk.vm.Collection; |
| |
| |
| import org.vmmagic.pragma.*; |
| import org.vmmagic.unboxed.*; |
| |
| /** |
| * This abstract class implements the global core functionality for all |
| * memory management schemes. All global MMTk plans should inherit from |
| * this class.<p> |
| * |
| * All plans make a clear distinction between <i>global</i> and |
| * <i>thread-local</i> activities, and divides global and local state |
| * into separate class hierarchies. Global activities must be |
| * synchronized, whereas no synchronization is required for |
| * thread-local activities. There is a single instance of Plan (or the |
| * appropriate sub-class), and a 1:1 mapping of PlanLocal to "kernel |
| * threads" (aka CPUs). Thus instance |
| * methods of PlanLocal allow fast, unsynchronized access to functions such as |
| * allocation and collection. |
| * |
| * The global instance defines and manages static resources |
| * (such as memory and virtual memory resources). This mapping of threads to |
| * instances is crucial to understanding the correctness and |
| * performance properties of MMTk plans. |
| */ |
| @Uninterruptible |
| public abstract class Plan implements Constants { |
| /**************************************************************************** |
| * Constants |
| */ |
| |
| /* GC State */ |
| public static final int NOT_IN_GC = 0; // this must be zero for C code |
| public static final int GC_PREPARE = 1; // before setup and obtaining root |
| public static final int GC_PROPER = 2; |
| |
| /* Polling */ |
| public static final int DEFAULT_POLL_FREQUENCY = (128 << 10) >> LOG_BYTES_IN_PAGE; |
| public static final int META_DATA_POLL_FREQUENCY = DEFAULT_POLL_FREQUENCY; |
| |
| /* Space Size Constants. */ |
| public static final boolean USE_CODE_SPACE = true; |
| public static final float PLOS_FRAC = 0.07f; |
| public static final int HEAP_FULL_MINIMUM = (1 << 17) >> LOG_BYTES_IN_PAGE; // 128K |
| public static final int HEAP_FULL_PERCENTAGE = 2; |
| |
| /* Allocator Constants */ |
| public static final int ALLOC_DEFAULT = 0; |
| public static final int ALLOC_NON_REFERENCE = 1; |
| public static final int ALLOC_NON_MOVING = 2; |
| public static final int ALLOC_IMMORTAL = 3; |
| public static final int ALLOC_LOS = 4; |
| public static final int ALLOC_PRIMITIVE_LOS = 5; |
| public static final int ALLOC_GCSPY = 6; |
| public static final int ALLOC_CODE = 7; |
| public static final int ALLOC_LARGE_CODE = 8; |
| public static final int ALLOC_HOT_CODE = USE_CODE_SPACE ? ALLOC_CODE : ALLOC_DEFAULT; |
| public static final int ALLOC_COLD_CODE = USE_CODE_SPACE ? ALLOC_CODE : ALLOC_DEFAULT; |
| public static final int ALLOC_STACK = ALLOC_LOS; |
| public static final int ALLOCATORS = 9; |
| public static final int DEFAULT_SITE = -1; |
| |
| /* Miscellaneous Constants */ |
| // public static final int LOS_SIZE_THRESHOLD = SegregatedFreeListSpace.MAX_CELL_SIZE; |
| public static final int NON_PARTICIPANT = 0; |
| public static final boolean GATHER_WRITE_BARRIER_STATS = false; |
| public static final int DEFAULT_MIN_NURSERY = (256 * 1024) >> LOG_BYTES_IN_PAGE; |
| public static final int DEFAULT_MAX_NURSERY = (32 << 20) >> LOG_BYTES_IN_PAGE; |
| public static final boolean SCAN_BOOT_IMAGE = true; // scan it for roots rather than trace it |
| public static final int MAX_COLLECTION_ATTEMPTS = 10; |
| // public static final boolean REQUIRES_LOS = VM.activePlan.constraints().requiresLOS(); |
| public static final int MAX_NON_LOS_DEFAULT_ALLOC_BYTES = VM.activePlan.constraints().maxNonLOSDefaultAllocBytes(); |
| public static final int MAX_NON_LOS_NONMOVING_ALLOC_BYTES = VM.activePlan.constraints().maxNonLOSNonMovingAllocBytes(); |
| public static final int MAX_NON_LOS_COPY_BYTES = VM.activePlan.constraints().maxNonLOSCopyBytes(); |
| |
| |
| /* Do we support a log bit in the object header? Some write barriers may use it */ |
| public static final boolean NEEDS_LOG_BIT_IN_HEADER = VM.activePlan.constraints().needsLogBitInHeader(); |
| |
| /**************************************************************************** |
| * Class variables |
| */ |
| |
| /** The space that holds any VM specific objects (e.g. a boot image) */ |
| public static final Space vmSpace = VM.memory.getVMSpace(); |
| |
| /** Any immortal objects allocated after booting are allocated here. */ |
| public static final ImmortalSpace immortalSpace = new ImmortalSpace("immortal", DEFAULT_POLL_FREQUENCY, VMRequest.create()); |
| |
| /** All meta data that is used by MMTk is allocated (and accounted for) in the meta data space. */ |
| public static final RawPageSpace metaDataSpace = new RawPageSpace("meta", DEFAULT_POLL_FREQUENCY, VMRequest.create()); |
| |
| /** Large objects are allocated into a special large object space. */ |
| public static final LargeObjectSpace loSpace = new LargeObjectSpace("los", DEFAULT_POLL_FREQUENCY, VMRequest.create()); |
| |
| /** Space used by the sanity checker (used at runtime only if sanity checking enabled */ |
| public static final RawPageSpace sanitySpace = new RawPageSpace("sanity", Integer.MAX_VALUE, VMRequest.create()); |
| |
| /** Space used to allocate objects that cannot be moved. we do not need a large space as the LOS is non-moving. */ |
| public static final MarkSweepSpace nonMovingSpace = new MarkSweepSpace("non-moving", DEFAULT_POLL_FREQUENCY, VMRequest.create()); |
| |
| public static final MarkSweepSpace smallCodeSpace = USE_CODE_SPACE ? new MarkSweepSpace("sm-code", DEFAULT_POLL_FREQUENCY, VMRequest.create()) : null; |
| public static final LargeObjectSpace largeCodeSpace = USE_CODE_SPACE ? new LargeObjectSpace("lg-code", DEFAULT_POLL_FREQUENCY, VMRequest.create()) : null; |
| |
| /* Space descriptors */ |
| public static final int IMMORTAL = immortalSpace.getDescriptor(); |
| public static final int VM_SPACE = vmSpace.getDescriptor(); |
| public static final int META = metaDataSpace.getDescriptor(); |
| public static final int LOS = loSpace.getDescriptor(); |
| public static final int SANITY = sanitySpace.getDescriptor(); |
| public static final int NON_MOVING = nonMovingSpace.getDescriptor(); |
| public static final int SMALL_CODE = USE_CODE_SPACE ? smallCodeSpace.getDescriptor() : 0; |
| public static final int LARGE_CODE = USE_CODE_SPACE ? largeCodeSpace.getDescriptor() : 0; |
| |
| /** Timer that counts total time */ |
| public static final Timer totalTime = new Timer("time"); |
| |
| /** Support for time-limited GCs */ |
| protected static long timeCap; |
| |
| /** Support for allocation-site identification */ |
| protected static int allocationSiteCount = 0; |
| |
| /** Global sanity checking state **/ |
| public static final SanityChecker sanityChecker = new SanityChecker(); |
| |
| /**************************************************************************** |
| * Constructor. |
| */ |
| public Plan() { |
| /* Create base option instances */ |
| Options.verbose = new Verbose(); |
| Options.verboseTiming = new VerboseTiming(); |
| Options.stressFactor = new StressFactor(); |
| Options.noFinalizer = new NoFinalizer(); |
| Options.noReferenceTypes = new NoReferenceTypes(); |
| Options.fullHeapSystemGC = new FullHeapSystemGC(); |
| Options.harnessAll = new HarnessAll(); |
| Options.ignoreSystemGC = new IgnoreSystemGC(); |
| Options.metaDataLimit = new MetaDataLimit(); |
| Options.nurserySize = new NurserySize(); |
| Options.variableSizeHeap = new VariableSizeHeap(); |
| Options.eagerMmapSpaces = new EagerMmapSpaces(); |
| Options.sanityCheck = new SanityCheck(); |
| Options.debugAddress = new DebugAddress(); |
| Options.perfEvents = new PerfEvents(); |
| Map.finalizeStaticSpaceMap(); |
| registerSpecializedMethods(); |
| } |
| |
| /**************************************************************************** |
| * Boot. |
| */ |
| |
| /** |
| * The boot method is called early in the boot process before any |
| * allocation. |
| */ |
| @Interruptible |
| public void boot() { |
| } |
| |
| /** |
| * The postBoot method is called by the runtime immediately after |
| * command-line arguments are available. Note that allocation must |
| * be supported prior to this point because the runtime |
| * infrastructure may require allocation in order to parse the |
| * command line arguments. For this reason all plans should operate |
| * gracefully on the default minimum heap size until the point that |
| * boot is called. |
| */ |
| @Interruptible |
| public void postBoot() { |
| VM.statistics.perfEventInit(Options.perfEvents.getValue()); |
| if (Options.verbose.getValue() > 2) Space.printVMMap(); |
| if (Options.verbose.getValue() > 3) VM.config.printConfig(); |
| if (Options.verbose.getValue() > 0) Stats.startAll(); |
| if (Options.eagerMmapSpaces.getValue()) Space.eagerlyMmapMMTkSpaces(); |
| } |
| |
| /** |
| * The fullyBooted method is called by the runtime just before normal |
| * execution commences. |
| */ |
| @Interruptible |
| public void fullyBooted() { |
| initialized = true; |
| if (Options.harnessAll.getValue()) harnessBegin(); |
| } |
| |
| /** |
| * The VM is about to exit. Perform any clean up operations. |
| * |
| * @param value The exit value |
| */ |
| @Interruptible |
| public void notifyExit(int value) { |
| if (Options.harnessAll.getValue()) harnessEnd(); |
| if (Options.verbose.getValue() == 1) { |
| Log.write("[End "); |
| totalTime.printTotalSecs(); |
| Log.writeln(" s]"); |
| } else if (Options.verbose.getValue() == 2) { |
| Log.write("[End "); |
| totalTime.printTotalMillis(); |
| Log.writeln(" ms]"); |
| } |
| if (Options.verboseTiming.getValue()) printDetailedTiming(true); |
| } |
| |
| /** |
| * Any Plan can override this to provide additional plan specific |
| * timing information. |
| * |
| * @param totals Print totals |
| */ |
| protected void printDetailedTiming(boolean totals) {} |
| |
| /** |
| * Perform any required write barrier action when installing an object reference |
| * a boot time. |
| * |
| * @param reference the reference value that is to be stored |
| * @return The raw value to be |
| */ |
| public Word bootTimeWriteBarrier(Word reference) { |
| return reference; |
| } |
| |
| /**************************************************************************** |
| * Allocation |
| */ |
| public static int getAllocationSite(boolean compileTime) { |
| if (compileTime) // a new allocation site is being compiled |
| return allocationSiteCount++; |
| else // an anonymous site |
| return DEFAULT_SITE; |
| } |
| |
| /**************************************************************************** |
| * Collection. |
| */ |
| |
| /** |
| * Perform a (global) collection phase. |
| */ |
| public abstract void collectionPhase(short phase); |
| |
| /** |
| * Replace a phase. |
| * |
| * @param oldScheduledPhase The scheduled phase to insert after |
| * @param scheduledPhase The scheduled phase to insert |
| */ |
| @Interruptible |
| public void replacePhase(int oldScheduledPhase, int scheduledPhase) { |
| VM.assertions.fail("replacePhase not implemented for this plan"); |
| } |
| |
| |
| /** |
| * Insert a phase. |
| * |
| * @param markerScheduledPhase The scheduled phase to insert after |
| * @param scheduledPhase The scheduled phase to insert |
| */ |
| @Interruptible |
| public void insertPhaseAfter(int markerScheduledPhase, int scheduledPhase) { |
| short tempPhase = Phase.createComplex("auto-gen", null, markerScheduledPhase, scheduledPhase); |
| replacePhase(markerScheduledPhase, Phase.scheduleComplex(tempPhase)); |
| } |
| |
| /** |
| * @return Whether last GC was an exhaustive attempt to collect the heap. For many collectors this is the same as asking whether the last GC was a full heap collection. |
| */ |
| public boolean lastCollectionWasExhaustive() { |
| return lastCollectionFullHeap(); |
| } |
| |
| /** |
| * @return Whether last GC is a full GC. |
| */ |
| public boolean lastCollectionFullHeap() { |
| return true; |
| } |
| |
| /** |
| * @return Is last GC a full collection? |
| */ |
| public static boolean isEmergencyCollection() { |
| return emergencyCollection; |
| } |
| |
| /** |
| * @return True if we have run out of heap space. |
| */ |
| public final boolean lastCollectionFailed() { |
| return !(collectionTrigger == Collection.EXTERNAL_GC_TRIGGER || |
| collectionTrigger == Collection.INTERNAL_PHASE_GC_TRIGGER) && |
| (getPagesAvail() < getHeapFullThreshold() || getPagesAvail() < requiredAtStart); |
| } |
| |
| /** |
| * Force the next collection to be full heap. |
| */ |
| public void forceFullHeapCollection() {} |
| |
| /** |
| * @return Is current GC only collecting objects allocated since last GC. |
| */ |
| public boolean isCurrentGCNursery() { |
| return false; |
| } |
| |
| private long lastStressPages = 0; |
| |
| /** |
| * Return the expected reference count. For non-reference counting |
| * collectors this becomes a true/false relationship. |
| * |
| * @param object The object to check. |
| * @param sanityRootRC The number of root references to the object. |
| * @return The expected (root excluded) reference count. |
| */ |
| public int sanityExpectedRC(ObjectReference object, int sanityRootRC) { |
| Space space = Space.getSpaceForObject(object); |
| return space.isReachable(object) ? SanityChecker.ALIVE : SanityChecker.DEAD; |
| } |
| |
| /** |
| * Perform a linear scan of all spaces to check for possible leaks. |
| * This is only called after a full-heap GC. |
| * |
| * @param scanner The scanner callback to use. |
| */ |
| public void sanityLinearScan(LinearScan scanner) { |
| } |
| |
| /** |
| * @return True is a stress test GC is required |
| */ |
| @Inline |
| public final boolean stressTestGCRequired() { |
| long pages = Space.cumulativeCommittedPages(); |
| if (initialized && |
| ((pages ^ lastStressPages) > Options.stressFactor.getPages())) { |
| lastStressPages = pages; |
| return true; |
| } else |
| return false; |
| } |
| |
| /**************************************************************************** |
| * GC State |
| */ |
| |
| protected static int requiredAtStart; |
| protected static int collectionTrigger; |
| protected static boolean emergencyCollection; |
| protected static boolean awaitingAsyncCollection; |
| protected static boolean stacksPrepared; |
| |
| private static boolean initialized = false; |
| private static boolean collectionTriggered; |
| @Entrypoint |
| private static int gcStatus = NOT_IN_GC; // shared variable |
| |
| /** @return Is the memory management system initialized? */ |
| public static boolean isInitialized() { |
| return initialized; |
| } |
| |
| /** |
| * Has collection has triggered? |
| */ |
| public static boolean isCollectionTriggered() { |
| return collectionTriggered; |
| } |
| |
| /** |
| * Set that a collection has been triggered. |
| */ |
| public static void setCollectionTriggered() { |
| collectionTriggered = true; |
| } |
| |
| /** |
| * A collection has fully completed. Clear the triggered flag. |
| */ |
| public static void collectionComplete() { |
| collectionTriggered = false; |
| } |
| |
| /** |
| * Return true if stacks have been prepared in this collection cycle. |
| * |
| * @return True if stacks have been prepared in this collection cycle. |
| */ |
| public static boolean stacksPrepared() { |
| return stacksPrepared; |
| } |
| /** |
| * Return true if a collection is in progress. |
| * |
| * @return True if a collection is in progress. |
| */ |
| public static boolean gcInProgress() { |
| return gcStatus != NOT_IN_GC; |
| } |
| |
| /** |
| * Return true if a collection is in progress and past the preparatory stage. |
| * |
| * @return True if a collection is in progress and past the preparatory stage. |
| */ |
| public static boolean gcInProgressProper() { |
| return gcStatus == GC_PROPER; |
| } |
| |
| /** |
| * Sets the GC status. |
| * |
| * @param s The new GC status. |
| */ |
| public static void setGCStatus(int s) { |
| if (gcStatus == NOT_IN_GC) { |
| /* From NOT_IN_GC to any phase */ |
| stacksPrepared = false; |
| if (Stats.gatheringStats()) { |
| Stats.startGC(); |
| VM.activePlan.global().printPreStats(); |
| } |
| } |
| VM.memory.isync(); |
| gcStatus = s; |
| VM.memory.sync(); |
| if (gcStatus == NOT_IN_GC) { |
| /* From any phase to NOT_IN_GC */ |
| if (Stats.gatheringStats()) { |
| Stats.endGC(); |
| VM.activePlan.global().printPostStats(); |
| } |
| } |
| } |
| |
| /** |
| * Print out statistics at the start of a GC |
| */ |
| public void printPreStats() { |
| if ((Options.verbose.getValue() == 1) || |
| (Options.verbose.getValue() == 2)) { |
| Log.write("[GC "); Log.write(Stats.gcCount()); |
| if (Options.verbose.getValue() == 1) { |
| Log.write(" Start "); |
| Plan.totalTime.printTotalSecs(); |
| Log.write(" s"); |
| } else { |
| Log.write(" Start "); |
| Plan.totalTime.printTotalMillis(); |
| Log.write(" ms"); |
| } |
| Log.write(" "); |
| Log.write(Conversions.pagesToKBytes(getPagesUsed())); |
| Log.write("KB "); |
| Log.flush(); |
| } |
| if (Options.verbose.getValue() > 2) { |
| Log.write("Collection "); Log.write(Stats.gcCount()); |
| Log.write(": "); |
| printUsedPages(); |
| Log.write(" Before Collection: "); |
| Space.printUsageMB(); |
| if (Options.verbose.getValue() >= 4) { |
| Log.write(" "); |
| Space.printUsagePages(); |
| } |
| if (Options.verbose.getValue() >= 5) { |
| Space.printVMMap(); |
| } |
| } |
| } |
| |
| /** |
| * Print out statistics at the end of a GC |
| */ |
| public final void printPostStats() { |
| if ((Options.verbose.getValue() == 1) || |
| (Options.verbose.getValue() == 2)) { |
| Log.write("-> "); |
| Log.writeDec(Conversions.pagesToBytes(getPagesUsed()).toWord().rshl(10)); |
| Log.write("KB "); |
| if (Options.verbose.getValue() == 1) { |
| totalTime.printLast(); |
| Log.writeln(" ms]"); |
| } else { |
| Log.write("End "); |
| totalTime.printTotal(); |
| Log.writeln(" ms]"); |
| } |
| } |
| if (Options.verbose.getValue() > 2) { |
| Log.write(" After Collection: "); |
| Space.printUsageMB(); |
| if (Options.verbose.getValue() >= 4) { |
| Log.write(" "); |
| Space.printUsagePages(); |
| } |
| if (Options.verbose.getValue() >= 5) { |
| Space.printVMMap(); |
| } |
| Log.write(" "); |
| printUsedPages(); |
| Log.write(" Collection time: "); |
| totalTime.printLast(); |
| Log.writeln(" ms"); |
| } |
| } |
| |
| public final void printUsedPages() { |
| Log.write("reserved = "); |
| Log.write(Conversions.pagesToMBytes(getPagesReserved())); |
| Log.write(" MB ("); |
| Log.write(getPagesReserved()); |
| Log.write(" pgs)"); |
| Log.write(" total = "); |
| Log.write(Conversions.pagesToMBytes(getTotalPages())); |
| Log.write(" MB ("); |
| Log.write(getTotalPages()); |
| Log.write(" pgs)"); |
| Log.writeln(); |
| } |
| |
| /** |
| * Set the collection trigger. |
| */ |
| public static void setCollectionTrigger(int trigger) { |
| collectionTrigger = trigger; |
| } |
| |
| /**************************************************************************** |
| * Harness |
| */ |
| protected static boolean insideHarness = false; |
| |
| /** |
| * Generic hook to allow benchmarks to be harnessed. A plan may use |
| * this to perform certain actions prior to the commencement of a |
| * benchmark, such as a full heap collection, turning on |
| * instrumentation, etc. By default we do a full heap GC, |
| * and then start stats collection. |
| */ |
| @Interruptible |
| public static void harnessBegin() { |
| // Save old values. |
| boolean oldFullHeap = Options.fullHeapSystemGC.getValue(); |
| boolean oldIgnore = Options.ignoreSystemGC.getValue(); |
| |
| // Set desired values. |
| Options.fullHeapSystemGC.setValue(true); |
| Options.ignoreSystemGC.setValue(false); |
| |
| // Trigger a full heap GC. |
| System.gc(); |
| |
| // Restore old values. |
| Options.ignoreSystemGC.setValue(oldIgnore); |
| Options.fullHeapSystemGC.setValue(oldFullHeap); |
| |
| // Start statistics |
| insideHarness = true; |
| Stats.startAll(); |
| } |
| |
| /** |
| * Generic hook to allow benchmarks to be harnessed. A plan may use |
| * this to perform certain actions after the completion of a |
| * benchmark, such as a full heap collection, turning off |
| * instrumentation, etc. By default we stop all statistics objects |
| * and print their values. |
| */ |
| @Interruptible |
| public static void harnessEnd() { |
| Stats.stopAll(); |
| insideHarness = false; |
| } |
| |
| /**************************************************************************** |
| * VM Accounting |
| */ |
| |
| /* Global accounting and static access */ |
| |
| /** |
| * Return the amount of <i>free memory</i>, in bytes (where free is |
| * defined as not in use). Note that this may overstate the amount |
| * of <i>available memory</i>, which must account for unused memory |
| * that is held in reserve for copying, and therefore unavailable |
| * for allocation. |
| * |
| * @return The amount of <i>free memory</i>, in bytes (where free is |
| * defined as not in use). |
| */ |
| public static Extent freeMemory() { |
| return totalMemory().minus(usedMemory()); |
| } |
| |
| /** |
| * Return the amount of <i>available memory</i>, in bytes. Note |
| * that this accounts for unused memory that is held in reserve |
| * for copying, and therefore unavailable for allocation. |
| * |
| * @return The amount of <i>available memory</i>, in bytes. |
| */ |
| public static Extent availableMemory() { |
| return totalMemory().minus(reservedMemory()); |
| } |
| |
| /** |
| * Return the amount of <i>memory in use</i>, in bytes. Note that |
| * this excludes unused memory that is held in reserve for copying, |
| * and therefore unavailable for allocation. |
| * |
| * @return The amount of <i>memory in use</i>, in bytes. |
| */ |
| public static Extent usedMemory() { |
| return Conversions.pagesToBytes(VM.activePlan.global().getPagesUsed()); |
| } |
| |
| |
| /** |
| * Return the amount of <i>memory in use</i>, in bytes. Note that |
| * this includes unused memory that is held in reserve for copying, |
| * and therefore unavailable for allocation. |
| * |
| * @return The amount of <i>memory in use</i>, in bytes. |
| */ |
| public static Extent reservedMemory() { |
| return Conversions.pagesToBytes(VM.activePlan.global().getPagesReserved()); |
| } |
| |
| /** |
| * Return the total amount of memory managed to the memory |
| * management system, in bytes. |
| * |
| * @return The total amount of memory managed to the memory |
| * management system, in bytes. |
| */ |
| public static Extent totalMemory() { |
| return HeapGrowthManager.getCurrentHeapSize(); |
| } |
| |
| /* Instance methods */ |
| |
| /** |
| * Return the total amount of memory managed to the memory |
| * management system, in pages. |
| * |
| * @return The total amount of memory managed to the memory |
| * management system, in pages. |
| */ |
| public final int getTotalPages() { |
| return totalMemory().toWord().rshl(LOG_BYTES_IN_PAGE).toInt(); |
| } |
| |
| /** |
| * Return the number of pages available for allocation. |
| * |
| * @return The number of pages available for allocation. |
| */ |
| public int getPagesAvail() { |
| return getTotalPages() - getPagesReserved(); |
| } |
| |
| /** |
| * Return the number of pages reserved for use given the pending |
| * allocation. Sub-classes must override the getCopyReserve method, |
| * as the arithmetic here is fixed. |
| * |
| * @return The number of pages reserved given the pending |
| * allocation, including space reserved for copying. |
| */ |
| public final int getPagesReserved() { |
| return getPagesUsed() + getCollectionReserve(); |
| } |
| |
| /** |
| * Return the number of pages reserved for collection. |
| * In most cases this is a copy reserve, all subclasses that |
| * manage a copying space must add the copying contribution. |
| * |
| * @return The number of pages reserved given the pending |
| * allocation, including space reserved for collection. |
| */ |
| public int getCollectionReserve() { |
| return 0; |
| } |
| |
| /** |
| * Return the number of pages reserved for use given the pending |
| * allocation. |
| * |
| * @return The number of pages reserved given the pending |
| * allocation, excluding space reserved for copying. |
| */ |
| public int getPagesUsed() { |
| return loSpace.reservedPages() + |
| immortalSpace.reservedPages() + metaDataSpace.reservedPages() + |
| nonMovingSpace.reservedPages(); |
| } |
| |
| /** |
| * Calculate the number of pages a collection is required to free to satisfy |
| * outstanding allocation requests. |
| * |
| * @return the number of pages a collection is required to free to satisfy |
| * outstanding allocation requests. |
| */ |
| public int getPagesRequired() { |
| return loSpace.requiredPages() + |
| metaDataSpace.requiredPages() + immortalSpace.requiredPages() + |
| nonMovingSpace.requiredPages(); |
| } |
| |
| /** |
| * The minimum number of pages a GC must have available after a collection |
| * for us to consider the collection successful. |
| */ |
| public int getHeapFullThreshold() { |
| int threshold = (getTotalPages() * HEAP_FULL_PERCENTAGE) / 100; |
| if (threshold < HEAP_FULL_MINIMUM) threshold = HEAP_FULL_MINIMUM; |
| return threshold; |
| } |
| |
| /** |
| * Return the number of metadata pages reserved for use given the pending |
| * allocation. |
| * |
| * @return The number of pages reserved given the pending |
| * allocation, excluding space reserved for copying. |
| */ |
| public int getMetaDataPagesUsed() { |
| return metaDataSpace.reservedPages(); |
| } |
| |
| /** |
| * Return the cycle time at which this GC should complete. |
| * |
| * @return The time cap for this GC (i.e. the time by which it |
| * should complete). |
| */ |
| public static long getTimeCap() { |
| return timeCap; |
| } |
| |
| /**************************************************************************** |
| * Internal read/write barriers. |
| */ |
| |
| /** |
| * Store an object reference |
| * |
| * @param slot The location of the reference |
| * @param value The value to store |
| */ |
| @Inline |
| public void storeObjectReference(Address slot, ObjectReference value) { |
| slot.store(value); |
| } |
| |
| /** |
| * Load an object reference |
| * |
| * @param slot The location of the reference |
| * @return the object reference loaded from slot |
| */ |
| @Inline |
| public ObjectReference loadObjectReference(Address slot) { |
| return slot.loadObjectReference(); |
| } |
| |
| /**************************************************************************** |
| * Collection. |
| */ |
| |
| /** |
| * This method is called periodically by the allocation subsystem |
| * (by default, each time a page is consumed), and provides the |
| * collector with an opportunity to collect. |
| * |
| * @param spaceFull Space request failed, must recover pages within 'space'. |
| * @param space The space that triggered the poll. |
| * @return true if a collection is required. |
| */ |
| @LogicallyUninterruptible |
| public final boolean poll(boolean spaceFull, Space space) { |
| if (isCollectionTriggered()) { |
| if (space == metaDataSpace) { |
| /* This is not, in general, in a GC safe point. */ |
| return false; |
| } |
| /* Someone else initiated a collection, we should join it */ |
| logPoll(space, "Joining collection"); |
| VM.collection.joinCollection(); |
| return true; |
| } |
| |
| if (collectionRequired(spaceFull)) { |
| if (space == metaDataSpace) { |
| /* In general we must not trigger a GC on metadata allocation since |
| * this is not, in general, in a GC safe point. Instead we initiate |
| * an asynchronous GC, which will occur at the next safe point. |
| */ |
| logPoll(space, "Asynchronous collection requested"); |
| setAwaitingAsyncCollection(); |
| return false; |
| } |
| logPoll(space, "Triggering collection"); |
| VM.collection.triggerCollection(Collection.RESOURCE_GC_TRIGGER); |
| return true; |
| } |
| |
| if (concurrentCollectionRequired()) { |
| logPoll(space, "Triggering collection"); |
| VM.collection.triggerCollection(Collection.INTERNAL_PHASE_GC_TRIGGER); |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Check whether an asynchronous collection is pending.<p> |
| * |
| * This is decoupled from the poll() mechanism because the |
| * triggering of asynchronous collections can trigger write |
| * barriers, which can trigger an asynchronous collection. Thus, if |
| * the triggering were tightly coupled with the request to alloc() |
| * within the write buffer code, then inifinite regress could |
| * result. There is no race condition in the following code since |
| * there is no harm in triggering the collection more than once, |
| * thus it is unsynchronized. |
| */ |
| @Inline |
| public static void checkForAsyncCollection() { |
| if (awaitingAsyncCollection && VM.collection.noThreadsInGC()) { |
| awaitingAsyncCollection = false; |
| VM.collection.triggerAsyncCollection(Collection.RESOURCE_GC_TRIGGER); |
| } |
| } |
| |
| /** Request an async GC */ |
| protected static void setAwaitingAsyncCollection() { |
| awaitingAsyncCollection = true; |
| } |
| |
| /** |
| * Log a message from within 'poll' |
| * @param space |
| * @param message |
| */ |
| private void logPoll(Space space, String message) { |
| if (Options.verbose.getValue() >= 3) { |
| Log.write(" [POLL] "); |
| Log.write(space.getName()); |
| Log.write(": "); |
| Log.writeln(message); |
| } |
| } |
| |
| /** |
| * This method controls the triggering of a GC. It is called periodically |
| * during allocation. Returns true to trigger a collection. |
| * |
| * @param spaceFull Space request failed, must recover pages within 'space'. |
| * @return True if a collection is requested by the plan. |
| */ |
| protected boolean collectionRequired(boolean spaceFull) { |
| boolean stressForceGC = stressTestGCRequired(); |
| boolean heapFull = getPagesReserved() > getTotalPages(); |
| |
| return spaceFull || stressForceGC || heapFull; |
| } |
| |
| /** |
| * This method controls the triggering of an atomic phase of a concurrent |
| * collection. It is called periodically during allocation. |
| * |
| * @return True if a collection is requested by the plan. |
| */ |
| protected boolean concurrentCollectionRequired() { |
| return false; |
| } |
| |
| /** |
| * Start GCspy server. |
| * |
| * @param port The port to listen on, |
| * @param wait Should we wait for a client to connect? |
| */ |
| @Interruptible |
| public void startGCspyServer(int port, boolean wait) { |
| VM.assertions.fail("startGCspyServer called on non GCspy plan"); |
| } |
| |
| /** |
| * Can this object ever move. Used by the VM to make decisions about |
| * whether it needs to copy IO buffers etc. |
| * |
| * @param object The object in question |
| * @return True if it is not possible that the object will ever move. |
| */ |
| public boolean willNeverMove(ObjectReference object) { |
| if (!VM.activePlan.constraints().movesObjects()) |
| return true; |
| if (Space.isInSpace(LOS, object)) |
| return true; |
| if (Space.isInSpace(IMMORTAL, object)) |
| return true; |
| if (Space.isInSpace(VM_SPACE, object)) |
| return true; |
| if (Space.isInSpace(NON_MOVING, object)) |
| return true; |
| if (USE_CODE_SPACE && Space.isInSpace(SMALL_CODE, object)) |
| return true; |
| if (USE_CODE_SPACE && Space.isInSpace(LARGE_CODE, object)) |
| return true; |
| /* |
| * Default to false- this preserves correctness over efficiency. |
| * Individual plans should override for non-moving spaces they define. |
| */ |
| return false; |
| } |
| |
| /**************************************************************************** |
| * Specialized Methods |
| */ |
| |
| /** |
| * Register specialized methods. |
| */ |
| @Interruptible |
| protected void registerSpecializedMethods() { |
| } |
| |
| /** |
| * Get the specialized scan with the given id. |
| */ |
| public final Class<?> getSpecializedScanClass(int id) { |
| return TransitiveClosure.getSpecializedScanClass(id); |
| } |
| } |