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