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