001 /* 002 * This file is part of the Jikes RVM project (http://jikesrvm.org). 003 * 004 * This file is licensed to You under the Eclipse Public License (EPL); 005 * You may not use this file except in compliance with the License. You 006 * may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/eclipse-1.0.php 009 * 010 * See the COPYRIGHT.txt file distributed with this work for information 011 * regarding copyright ownership. 012 */ 013 package org.mmtk.utility.heap; 014 015 import org.mmtk.policy.Space; 016 import org.mmtk.utility.Constants; 017 import org.mmtk.utility.options.ProtectOnRelease; 018 import org.mmtk.utility.options.Options; 019 020 import org.mmtk.vm.Lock; 021 import org.mmtk.vm.VM; 022 023 import org.vmmagic.pragma.*; 024 import org.vmmagic.unboxed.*; 025 026 /** 027 * This class manages the allocation of pages for a space. When a 028 * page is requested by the space both a page budget and the use of 029 * virtual address space are checked. If the request for space can't 030 * be satisfied (for either reason) a GC may be triggered.<p> 031 * 032 * This class is abstract, and is subclassed with monotone and 033 * freelist variants, which reflect monotonic and ad hoc space usage 034 * respectively. Monotonic use is easier to manage, but is obviously 035 * more restrictive (useful for copying collectors which allocate 036 * monotonically before freeing the entire space and starting over). 037 */ 038 @Uninterruptible 039 public abstract class PageResource implements Constants { 040 041 /**************************************************************************** 042 * 043 * Class variables 044 */ 045 046 /** 047 * 048 */ 049 protected static final boolean ZERO_ON_RELEASE = false; // debugging 050 051 private static final Lock classLock; 052 private static long cumulativeCommitted = 0; 053 054 055 /**************************************************************************** 056 * 057 * Instance variables 058 */ 059 060 // page budgeting 061 062 /** 063 * 064 */ 065 protected int reserved; 066 protected int committed; 067 068 protected final boolean contiguous; 069 protected final Space space; 070 071 /** only for contiguous spaces */ 072 protected Address start; 073 074 // locking 075 private final Lock lock; 076 077 // zeroing 078 protected boolean zeroNT; 079 protected boolean zeroConcurrent; 080 protected ConcurrentZeroingContext zeroingContext; 081 082 /**************************************************************************** 083 * 084 * Initialization 085 */ 086 static { 087 classLock = VM.newLock("PageResource"); 088 Options.protectOnRelease = new ProtectOnRelease(); 089 } 090 091 /** 092 * Constructor 093 * 094 * @param space The space to which this resource is attached 095 */ 096 private PageResource(Space space, boolean contiguous) { 097 this.contiguous = contiguous; 098 this.space = space; 099 lock = VM.newLock(space.getName() + ".lock"); 100 } 101 102 /** 103 * Constructor for discontiguous spaces 104 * 105 * @param space The space to which this resource is attached 106 */ 107 PageResource(Space space) { 108 this(space, false); 109 } 110 111 /** 112 * Constructor for contiguous spaces 113 * 114 * @param space The space to which this resource is attached 115 */ 116 PageResource(Space space, Address start) { 117 this(space, true); 118 this.start = start; 119 } 120 121 /** 122 * Return the number of available physical pages for this resource. 123 * This includes all pages currently unused by this resource's page 124 * cursor. If the resource is using discontiguous space it also includes 125 * currently unassigned discontiguous space.<p> 126 * 127 * Note: This just considers physical pages (ie virtual memory pages 128 * allocated for use by this resource). This calculation is orthogonal 129 * to and does not consider any restrictions on the number of pages 130 * this resource may actually use at any time (ie the number of 131 * committed and reserved pages).<p> 132 * 133 * Note: The calculation is made on the assumption that all space that 134 * could be assigned to this resource would be assigned to this resource 135 * (ie the unused discontiguous space could just as likely be assigned 136 * to another competing resource). 137 * 138 * @return The number of available physical pages for this resource. 139 */ 140 public abstract int getAvailablePhysicalPages(); 141 142 /** 143 * Reserve pages.<p> 144 * 145 * The role of reserving pages is that it allows the request to be 146 * noted as pending (the difference between committed and reserved 147 * indicates pending requests). If the request would exceed the 148 * page budget then the caller must poll in case a GC is necessary. 149 * 150 * @param pages The number of pages requested 151 * @return The actual number of pages reserved (including metadata, etc.) 152 */ 153 @Inline 154 public final int reservePages(int pages) { 155 lock(); 156 pages = adjustForMetaData(pages); 157 reserved += pages; 158 unlock(); 159 return pages; 160 } 161 162 /** 163 * Remove a request to the space. 164 * 165 * @param reservedPages The number of pages returned due to the request. 166 */ 167 @Inline 168 public final void clearRequest(int reservedPages) { 169 lock(); 170 reserved -= reservedPages; 171 unlock(); 172 } 173 174 /** 175 * Update the zeroing approach for this page resource. 176 */ 177 @Interruptible 178 public void updateZeroingApproach(boolean nontemporal, boolean concurrent) { 179 if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(!concurrent || contiguous); 180 this.zeroNT = nontemporal; 181 this.zeroConcurrent = concurrent; 182 if (concurrent) { 183 this.zeroingContext = new ConcurrentZeroingContext(this); 184 VM.collection.spawnCollectorContext(zeroingContext); 185 } 186 } 187 188 /** 189 * Skip concurrent zeroing (fall back to bulk zeroing). 190 */ 191 public void skipConcurrentZeroing() { 192 // Clearing this flag has the effect of reverting back to bulk zeroing. 193 zeroConcurrent = false; 194 } 195 196 /** 197 * Trigger concurrent zeroing. 198 */ 199 public void triggerConcurrentZeroing() { 200 zeroConcurrent = true; 201 this.zeroingContext.trigger(); 202 } 203 204 /** 205 * The entry point for the concurrent zeroing context. 206 */ 207 public void concurrentZeroing() { 208 VM.assertions.fail("This PageResource does not implement concurrent zeroing"); 209 } 210 211 abstract Address allocPages(int reservedPages, int requiredPages, boolean zeroed); 212 213 /** 214 * Adjust a page request to include metadata requirements for a request 215 * of the given size. This must be a pure function, that is it does not 216 * depend on the state of the PageResource. 217 * 218 * @param pages The size of the pending allocation in pages 219 * @return The number of required pages, inclusive of any metadata 220 */ 221 public abstract int adjustForMetaData(int pages); 222 223 /** 224 * Allocate pages in virtual memory, returning zero on failure.<p> 225 * 226 * If the request cannot be satisfied, zero is returned and it 227 * falls to the caller to trigger the GC. 228 * 229 * Call <code>allocPages</code> (subclass) to find the pages in 230 * virtual memory. If successful then commit the pending page 231 * request and return the address of the first page. 232 * 233 * @param pagesReserved The number of pages reserved by the initial request 234 * @param pages The number of pages requested 235 * @param zeroed If true allocated pages are zeroed. 236 * @return The address of the first of <code>pages</code> pages, or 237 * zero on failure. 238 */ 239 @Inline 240 public final Address getNewPages(int pagesReserved, int pages, boolean zeroed) { 241 return allocPages(pagesReserved, pages, zeroed); 242 } 243 244 /** 245 * Commit pages to the page budget. This is called after 246 * successfully determining that the request can be satisfied by 247 * both the page budget and virtual memory. This simply accounts 248 * for the discrepancy between <code>committed</code> and 249 * <code>reserved</code> while the request was pending. 250 * 251 * This *MUST* be called by each PageResource during the 252 * allocPages, and the caller must hold the lock. 253 * 254 * @param reservedPages The number of pages initially reserved due to this request 255 * @param actualPages The number of pages actually allocated. 256 */ 257 protected void commitPages(int reservedPages, int actualPages) { 258 int delta = actualPages - reservedPages; 259 reserved += delta; 260 committed += actualPages; 261 if (VM.activePlan.isMutator()) { 262 // only count mutator pages 263 addToCommitted(actualPages); 264 } 265 } 266 267 /** 268 * Return the number of reserved pages 269 * 270 * @return The number of reserved pages. 271 */ 272 public final int reservedPages() { return reserved; } 273 274 /** 275 * Return the number of committed pages 276 * 277 * @return The number of committed pages. 278 */ 279 public final int committedPages() { return committed; } 280 281 /** 282 * Return the cumulative number of committed pages 283 * 284 * @return The cumulative number of committed pages. 285 */ 286 public static long cumulativeCommittedPages() { return cumulativeCommitted; } 287 288 /** 289 * Add to the total cumulative committed page count. 290 * 291 * @param pages The number of pages to be added. 292 */ 293 private static void addToCommitted(int pages) { 294 classLock.acquire(); 295 cumulativeCommitted += pages; 296 classLock.release(); 297 } 298 299 /** 300 * Acquire the lock. 301 */ 302 protected final void lock() { 303 lock.acquire(); 304 } 305 306 /** 307 * Release the lock. 308 */ 309 protected final void unlock() { 310 lock.release(); 311 } 312 }