/* * 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.utility.heap; import org.mmtk.plan.Plan; import org.mmtk.policy.Space; import org.mmtk.utility.Constants; import org.mmtk.utility.options.ProtectOnRelease; import org.mmtk.utility.options.Options; import org.mmtk.vm.Lock; import org.mmtk.vm.VM; import org.vmmagic.pragma.*; import org.vmmagic.unboxed.*; /** * This class manages the allocation of pages for a space. When a * page is requested by the space both a page budget and the use of * virtual address space are checked. If the request for space can't * be satisfied (for either reason) a GC may be triggered.<p> * * This class is abstract, and is subclassed with monotone and * freelist variants, which reflect monotonic and ad hoc space usage * respectively. Monotonic use is easier to manage, but is obviously * more restrictive (useful for copying collectors which allocate * monotonically before freeing the entire space and starting over). */ @Uninterruptible public abstract class PageResource implements Constants { /**************************************************************************** * * Class variables */ protected static final boolean ZERO_ON_RELEASE = false; // debugging private static final Lock classLock; private static long cumulativeCommitted = 0; /**************************************************************************** * * Instance variables */ // page budgeting protected int reserved; protected int committed; protected int required; private final int pageBudget; protected final boolean contiguous; protected final Space space; protected Address start; // only for contiguous // locking private final Lock gcLock; // used during GC private final Lock mutatorLock; // used by mutators /**************************************************************************** * * Initialization */ static { classLock = VM.newLock("PageResource"); Options.protectOnRelease = new ProtectOnRelease(); } /** * Constructor * * @param pageBudget The budget of pages available to this memory * manager before it must poll the collector. * @param space The space to which this resource is attached */ private PageResource(int pageBudget, Space space, boolean contiguous) { this.pageBudget = pageBudget; this.contiguous = contiguous; this.space = space; gcLock = VM.newLock(space.getName() + ".gcLock"); mutatorLock = VM.newLock(space.getName() + ".mutatorLock"); } /** * Constructor for discontiguous spaces * * @param pageBudget The budget of pages available to this memory * manager before it must poll the collector. * @param space The space to which this resource is attached */ PageResource(int pageBudget, Space space) { this(pageBudget, space, false); } /** * Constructor for contiguous spaces * * @param pageBudget The budget of pages available to this memory * manager before it must poll the collector. * @param space The space to which this resource is attached */ PageResource(int pageBudget, Space space, Address start) { this(pageBudget, space, true); this.start = start; } /** * Return the number of available physical pages for this resource. * * Note: This just considers physical pages (ie virtual memory pages * allocated for use by this resource). This calculation is orthogonal * to and does not consider any restrictions on the number of pages * this resource may actually use at any time (ie the number of * committed and reserved pages).<p> * * Note: The calculation is made on the assumption that all space that * could be assigned to this resource would be assigned to this resource * (ie the unused discontiguous space could just as likely be assigned * to another competing resource). * * @return The number of available physical pages for this resource. */ public abstract int getAvailablePhysicalPages(); /** * Reserve pages.<p> * * The role of reserving pages is that it allows the request to be * noted as pending (the difference between committed and reserved * indicates pending requests). If the request would exceed the * page budget then the caller must poll in case a GC is necessary. * * @param pages The number of pages requested * @return True if the page budget could satisfy the request. */ @Inline public final boolean reservePages(int pages) { lock(); required += adjustForMetaData(pages); reserved = committed + required; boolean satisfied = reserved <= pageBudget; unlock(); return satisfied; } /** * Remove a request to the space. * * @param pages The number of pages in the request. */ @Inline public final void clearRequest(int pages) { lock(); required -= adjustForMetaData(pages); unlock(); } /** * Reserve pages unconditionally.<p> * * An example of where this is useful is in cases where it is * desirable to put some space aside as head-room. By * unconditionally reserving the pages, the pages are counted * against the collectors budget. When the space is actually * needed, the pages can be unconditionally released, freeing * the pages for other purposes. * * @param pages The number of pages to be unconditionally * reserved. */ public final void unconditionallyReservePages(int pages) { lock(); committed += pages; reserved += pages; unlock(); } /** * Release pages unconditionally.<p> * * This call allows pages to be unconditionally removed from * the collectors page budget. * * @see #unconditionallyReservePages * @param pages The number of pages to be unconditionally * released. */ public final void unconditionallyReleasePages(int pages) { lock(); committed -= pages; reserved -= pages; unlock(); } abstract Address allocPages(int pages); /** * Adjust a page request to include metadata requirements for a request * of the given size. This must be a pure function, that is it does not * depend on the state of the PageResource. * * @param pages The size of the pending allocation in pages * @return The number of required pages, inclusive of any metadata */ public abstract int adjustForMetaData(int pages); /** * Allocate pages in virtual memory, returning zero on failure.<p> * * If the request cannot be satisfied, zero is returned and it * falls to the caller to trigger the GC. * * Call <code>allocPages</code> (subclass) to find the pages in * virtual memory. If successful then commit the pending page * request and return the address of the first page. * * @param pages The number of pages requested * @return The address of the first of <code>pages</code> pages, or * zero on failure. */ @Inline public final Address getNewPages(int pages) { return allocPages(pages); } /** * Commit pages to the page budget. This is called after * successfully determining that the request can be satisfied by * both the page budget and virtual memory. This simply accounts * for the descrepency between <code>committed</code> and * <code>reserved</code> while the request was pending. * * This *MUST* be called by each PageResource during the * allocPages, and the caller must hold the lock. * * @param requestedPages The number of pages from this request * @param totalPages The number of pages */ protected void commitPages(int requestedPages, int totalPages) { int predictedPages = adjustForMetaData(requestedPages); int delta = totalPages - predictedPages; required -= predictedPages; reserved += delta; committed += totalPages; if (!Plan.gcInProgress()) addToCommitted(totalPages); // only count mutator pages } /** * Return the number of reserved pages * * @return The number of reserved pages. */ public final int reservedPages() { return reserved; } /** * Return the number of committed pages * * @return The number of committed pages. */ public final int committedPages() { return committed; } /** * Return the number of required pages * * @return The number of required pages. */ public final int requiredPages() { return required; } /** * Return the cumulative number of committed pages * * @return The cumulative number of committed pages. */ public static long cumulativeCommittedPages() { return cumulativeCommitted; } /** * Add to the total cumulative committed page count. * * @param pages The number of pages to be added. */ private static void addToCommitted(int pages) { classLock.acquire(); cumulativeCommitted += pages; classLock.release(); } /** * Acquire the appropriate lock depending on whether the context is * GC or mutator. */ protected final void lock() { if (Plan.gcInProgress()) gcLock.acquire(); else mutatorLock.acquire(); } /** * Release the appropriate lock depending on whether the context is * GC or mutator. */ protected final void unlock() { if (Plan.gcInProgress()) gcLock.release(); else mutatorLock.release(); } }