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.plan.generational;
014
015import static org.mmtk.utility.Constants.LOG_BYTES_IN_PAGE;
016
017import org.mmtk.plan.*;
018import org.mmtk.policy.CopySpace;
019import org.mmtk.policy.Space;
020
021import org.mmtk.utility.deque.*;
022import org.mmtk.utility.heap.Map;
023import org.mmtk.utility.heap.VMRequest;
024import org.mmtk.utility.Log;
025import org.mmtk.utility.options.Options;
026import org.mmtk.utility.sanitychecker.SanityChecker;
027import org.mmtk.utility.statistics.*;
028
029import org.mmtk.vm.VM;
030
031import org.vmmagic.pragma.*;
032import org.vmmagic.unboxed.*;
033
034/**
035 * This abstract class implements the core functionality of generic
036 * two-generation copying collectors.  Nursery collections occur when
037 * either the heap is full or the nursery is full.  The nursery size
038 * is determined by an optional command line argument.  If undefined,
039 * the nursery size is "infinite", so nursery collections only occur
040 * when the heap is full (this is known as a flexible-sized nursery
041 * collector).  Thus both fixed and flexible nursery sizes are
042 * supported.  Full heap collections occur when the nursery size has
043 * dropped to a statically defined threshold,
044 * <code>NURSERY_THRESHOLD</code><p>
045 *
046 * See also Plan.java for general comments on local vs global plan
047 * classes.
048 */
049@Uninterruptible
050public abstract class Gen extends StopTheWorld {
051
052  /*****************************************************************************
053   *
054   * Constants
055   */
056
057  /**
058   *
059   */
060  protected static final float SURVIVAL_ESTIMATE = 0.8f; // est yield
061  protected static final float MATURE_FRACTION = 0.5f; // est yield
062  private static final float WORST_CASE_COPY_EXPANSION = 1.5f; // worst case for addition of one word overhead due to address based hashing
063  public static final boolean IGNORE_REMSETS = false;
064  public static final boolean USE_NON_HEAP_OBJECT_REFERENCE_WRITE_BARRIER = false;
065  public static final boolean USE_OBJECT_BARRIER_FOR_AASTORE = false; // choose between slot and object barriers
066  public static final boolean USE_OBJECT_BARRIER_FOR_PUTFIELD = false; // choose between slot and object barriers
067  public static final boolean USE_OBJECT_BARRIER = USE_OBJECT_BARRIER_FOR_AASTORE || USE_OBJECT_BARRIER_FOR_PUTFIELD;
068
069  /** Fraction of available virtual memory to give to the nursery (if contiguous) */
070  protected static final float NURSERY_VM_FRACTION = 0.15f;
071
072  /** Switch between a contiguous and discontiguous nursery (experimental) */
073  static final boolean USE_DISCONTIGUOUS_NURSERY = false;
074
075  // Allocators
076  public static final int ALLOC_NURSERY        = ALLOC_DEFAULT;
077  public static final int ALLOC_MATURE         = StopTheWorld.ALLOCATORS + 1;
078  public static final int ALLOC_MATURE_MINORGC = StopTheWorld.ALLOCATORS + 2;
079  public static final int ALLOC_MATURE_MAJORGC = StopTheWorld.ALLOCATORS + 3;
080
081  public static final int SCAN_NURSERY = 0;
082  public static final int SCAN_MATURE  = 1;
083
084  /*****************************************************************************
085   *
086   * Class fields
087   */
088
089  /**
090   *
091   */
092
093  /* Statistics */
094  protected static final BooleanCounter fullHeap = new BooleanCounter("majorGC", true, true);
095  private static final Timer fullHeapTime = new Timer("majorGCTime", false, true);
096  protected static final EventCounter wbFast;
097  protected static final EventCounter wbSlow;
098  public static final SizeCounter nurseryMark;
099  public static final SizeCounter nurseryCons;
100
101  /* The nursery space is where all new objects are allocated by default */
102  private static final VMRequest vmRequest = USE_DISCONTIGUOUS_NURSERY ? VMRequest.discontiguous() : VMRequest.highFraction(NURSERY_VM_FRACTION);
103  public static final CopySpace nurserySpace = new CopySpace("nursery", false, vmRequest);
104
105  public static final int NURSERY = nurserySpace.getDescriptor();
106  private static final Address NURSERY_START = nurserySpace.getStart();
107
108  /*****************************************************************************
109   *
110   * Instance fields
111   */
112
113  /* status fields */
114
115  /**
116   *
117   */
118  public boolean gcFullHeap = false;
119  public boolean nextGCFullHeap = false;
120
121  /* The trace object */
122  public final Trace nurseryTrace = new Trace(metaDataSpace);
123
124  /**
125   * Remset pools
126   */
127
128  /**
129   *
130   */
131  public final SharedDeque modbufPool = new SharedDeque("modBufs",metaDataSpace, 1);
132  public final SharedDeque remsetPool = new SharedDeque("remSets",metaDataSpace, 1);
133  public final SharedDeque arrayRemsetPool = new SharedDeque("arrayRemSets",metaDataSpace, 2);
134
135  /*
136   * Class initializer
137   */
138  static {
139    if (GATHER_WRITE_BARRIER_STATS) {
140      wbFast = new EventCounter("wbFast");
141      wbSlow = new EventCounter("wbSlow");
142    } else {
143      wbFast = null;
144      wbSlow = null;
145    }
146    if (Stats.GATHER_MARK_CONS_STATS) {
147      nurseryMark = new SizeCounter("nurseryMark", true, true);
148      nurseryCons = new SizeCounter("nurseryCons", true, true);
149    } else {
150      nurseryMark = null;
151      nurseryCons = null;
152    }
153  }
154
155  /*****************************************************************************
156   *
157   * Collection
158   */
159
160  /**
161   * {@inheritDoc}
162   */
163  @Override
164  public void forceFullHeapCollection() {
165    nextGCFullHeap = true;
166  }
167
168  @Override
169  @NoInline
170  public void collectionPhase(short phaseId) {
171    if (phaseId == SET_COLLECTION_KIND) {
172      super.collectionPhase(phaseId);
173      gcFullHeap = requiresFullHeapCollection();
174      return;
175    }
176
177    if (phaseId == PREPARE) {
178      nurserySpace.prepare(true);
179      if (traceFullHeap()) {
180        if (gcFullHeap) {
181          if (Stats.gatheringStats()) fullHeap.set();
182          fullHeapTime.start();
183        }
184        super.collectionPhase(phaseId);
185
186        // we can throw away the remsets (but not modbuf) for a full heap GC
187        remsetPool.clearDeque(1);
188        arrayRemsetPool.clearDeque(2);
189      }
190      return;
191    }
192
193    if (phaseId == STACK_ROOTS) {
194      VM.scanning.notifyInitialThreadScanComplete(!traceFullHeap());
195      setGCStatus(GC_PROPER);
196      return;
197    }
198
199    if (phaseId == CLOSURE) {
200      if (!traceFullHeap()) {
201        nurseryTrace.prepare();
202      }
203      return;
204    }
205
206    if (phaseId == RELEASE) {
207      nurserySpace.release();
208      switchNurseryZeroingApproach(nurserySpace);
209      modbufPool.clearDeque(1);
210      remsetPool.clearDeque(1);
211      arrayRemsetPool.clearDeque(2);
212      if (!traceFullHeap()) {
213        nurseryTrace.release();
214      } else {
215        super.collectionPhase(phaseId);
216        if (gcFullHeap) fullHeapTime.stop();
217      }
218      nextGCFullHeap = (getPagesAvail() < Options.nurserySize.getMinNursery());
219      return;
220    }
221
222    super.collectionPhase(phaseId);
223  }
224
225  @Override
226  public final boolean collectionRequired(boolean spaceFull, Space space) {
227    int availableNurseryPages = Options.nurserySize.getMaxNursery() - nurserySpace.reservedPages();
228
229    /* periodically recalculate nursery pretenure threshold */
230    Plan.pretenureThreshold = (int) ((availableNurseryPages << LOG_BYTES_IN_PAGE) * Options.pretenureThresholdFraction.getValue());
231
232    if (availableNurseryPages <= 0) {
233      return true;
234    }
235
236    if (virtualMemoryExhausted()) {
237      return true;
238    }
239
240    if (spaceFull && space != nurserySpace) {
241      nextGCFullHeap = true;
242    }
243
244    return super.collectionRequired(spaceFull, space);
245  }
246
247  /**
248   * Determine if this GC should be a full heap collection.
249   *
250   * @return <code>true</code> is this GC should be a full heap collection.
251   */
252  protected boolean requiresFullHeapCollection() {
253    if (userTriggeredCollection && Options.fullHeapSystemGC.getValue()) {
254      return true;
255    }
256
257    if (nextGCFullHeap || collectionAttempt > 1) {
258      // Forces full heap collection
259      return true;
260    }
261
262    if (virtualMemoryExhausted()) {
263      return true;
264    }
265
266    return false;
267  }
268
269  /**
270   * Independent of how many pages remain in the page budget (a function of
271   * heap size), we must ensure we never exhaust virtual memory.  Therefore
272   * we must never let the nursery grow to the extent that it can't be
273   * copied into the mature space.
274   *
275   * @return {@code true} if the nursery has grown to the extent that it may not be
276   * able to be copied into the mature space.
277   */
278  private boolean virtualMemoryExhausted() {
279    return ((int)(getCollectionReserve() * WORST_CASE_COPY_EXPANSION)) >= getMaturePhysicalPagesAvail();
280  }
281
282  /*****************************************************************************
283   *
284   * Correctness
285   */
286
287  /*****************************************************************************
288   *
289   * Accounting
290   */
291
292  /**
293   * {@inheritDoc}
294   * Simply add the nursery's contribution to that of
295   * the superclass.
296   */
297  @Override
298  public int getPagesUsed() {
299    return (nurserySpace.reservedPages() + super.getPagesUsed());
300  }
301
302  /**
303   * Return the number of pages available for allocation, <i>assuming
304   * all future allocation is to the nursery</i>.
305   *
306   * @return The number of pages available for allocation, <i>assuming
307   * all future allocation is to the nursery</i>.
308   */
309  @Override
310  public int getPagesAvail() {
311    return super.getPagesAvail() >> 1;
312  }
313
314  /**
315   * Return the number of pages reserved for collection.
316   */
317  @Override
318  public int getCollectionReserve() {
319    return nurserySpace.reservedPages() + super.getCollectionReserve();
320  }
321
322  /**
323   * Return the number of pages available for allocation into the mature
324   * space.
325   *
326   * @return The number of pages available for allocation into the mature
327   * space.
328   */
329  public abstract int getMaturePhysicalPagesAvail();
330
331  /*****************************************************************************
332   *
333   * Miscellaneous
334   */
335
336  /**
337   * Return {@code true} if the address resides within the nursery
338   *
339   * @param addr The object to be tested
340   * @return {@code true} if the address resides within the nursery
341   */
342  @Inline
343  static boolean inNursery(Address addr) {
344    if (USE_DISCONTIGUOUS_NURSERY)
345      return Map.getDescriptorForAddress(addr) == NURSERY;
346    else
347      return addr.GE(NURSERY_START);
348  }
349
350  /**
351   * Return {@code true} if the object resides within the nursery
352   *
353   * @param obj The object to be tested
354   * @return {@code true} if the object resides within the nursery
355   */
356  @Inline
357  static boolean inNursery(ObjectReference obj) {
358    return inNursery(obj.toAddress());
359  }
360
361  /**
362   * @return Does the mature space do copying ?
363   */
364  protected boolean copyMature() {
365    return false;
366  }
367
368  /**
369   * Print pre-collection statistics. In this class we prefix the output
370   * indicating whether the collection was full heap or not.
371   */
372  @Override
373  public void printPreStats() {
374    if ((Options.verbose.getValue() >= 1) && (gcFullHeap))
375      Log.write("[Full heap]");
376    super.printPreStats();
377  }
378
379  /**
380   * Accessor method to allow the generic generational code in Gen.java
381   * to access the mature space.
382   *
383   * @return The mature space, set by each subclass of <code>Gen</code>.
384   */
385  protected abstract Space activeMatureSpace();
386
387  /**
388   * @return {@code true} if we should trace the whole heap during collection. True if
389   *         we're ignoring remsets or if we're doing a full heap GC.
390   */
391  public final boolean traceFullHeap() {
392    return IGNORE_REMSETS || gcFullHeap;
393  }
394
395  @Override
396  public final boolean isCurrentGCNursery() {
397    return !(IGNORE_REMSETS || gcFullHeap);
398  }
399
400  @Override
401  public final boolean lastCollectionFullHeap() {
402    return gcFullHeap;
403  }
404
405  @Override
406  public boolean willNeverMove(ObjectReference object) {
407    if (Space.isInSpace(NURSERY, object))
408      return false;
409    return super.willNeverMove(object);
410  }
411
412  @Override
413  public int sanityExpectedRC(ObjectReference object, int sanityRootRC) {
414    Space space = Space.getSpaceForObject(object);
415
416    // Nursery
417    if (space == Gen.nurserySpace) {
418      return SanityChecker.DEAD;
419    }
420
421    // Immortal spaces
422    if (space == Gen.immortalSpace || space == Gen.vmSpace) {
423      return space.isReachable(object) ? SanityChecker.ALIVE : SanityChecker.DEAD;
424    }
425
426    // Mature space (nursery collection)
427    if (VM.activePlan.global().isCurrentGCNursery()) {
428      return SanityChecker.UNSURE;
429    }
430
431    // Mature space (full heap collection)
432    return space.isReachable(object) ? SanityChecker.ALIVE : SanityChecker.DEAD;
433  }
434
435  @Override
436  @Interruptible
437  protected void registerSpecializedMethods() {
438    TransitiveClosure.registerSpecializedScan(SCAN_NURSERY, GenNurseryTraceLocal.class);
439    super.registerSpecializedMethods();
440  }
441
442  @Interruptible
443  @Override
444  public void fullyBooted() {
445    super.fullyBooted();
446    nurserySpace.setZeroingApproach(Options.nurseryZeroing.getNonTemporal(), Options.nurseryZeroing.getConcurrent());
447  }
448}