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.policy;
014
015import org.mmtk.plan.TransitiveClosure;
016import org.mmtk.utility.heap.*;
017import org.mmtk.utility.options.Options;
018import org.mmtk.utility.options.MarkSweepMarkBits;
019import org.mmtk.utility.options.EagerCompleteSweep;
020import org.mmtk.utility.HeaderByte;
021
022import org.mmtk.vm.VM;
023
024import org.vmmagic.pragma.*;
025import org.vmmagic.unboxed.*;
026
027/**
028 * Each instance of this class corresponds to one mark-sweep *space*.
029 * Each of the instance methods of this class may be called by any
030 * thread (i.e. synchronization must be explicit in any instance or
031 * class method).  This contrasts with the MarkSweepLocal, where
032 * instances correspond to *plan* instances and therefore to kernel
033 * threads.  Thus unlike this class, synchronization is not necessary
034 * in the instance methods of MarkSweepLocal.
035 */
036@Uninterruptible
037public final class MarkSweepSpace extends SegregatedFreeListSpace {
038
039  /****************************************************************************
040   *
041   * Class variables
042   */
043
044  /**
045   * Select between using mark bits in a side bitmap, or mark bits
046   * in the headers of object (or other sub-class scheme), and a single
047   * mark bit per block.
048   */
049  public static final boolean HEADER_MARK_BITS = VM.config.HEADER_MARK_BITS;
050  /** highest bit bits we may use */
051  private static final int AVAILABLE_LOCAL_BITS = 8 - HeaderByte.USED_GLOBAL_BITS;
052
053  /* mark bits */
054  private static final int COUNT_BASE = 0;
055
056  public static final int DEFAULT_MARKCOUNT_BITS = 4;
057  public static final int MAX_MARKCOUNT_BITS = AVAILABLE_LOCAL_BITS - COUNT_BASE;
058  private static final byte MARK_COUNT_INCREMENT = (byte) (1 << COUNT_BASE);
059  private static final byte MARK_COUNT_MASK = (byte) (((1 << MAX_MARKCOUNT_BITS) - 1) << COUNT_BASE);
060
061  private static final boolean EAGER_MARK_CLEAR = HeaderByte.NEEDS_UNLOGGED_BIT;
062
063  /* header requirements */
064  public static final int LOCAL_GC_BITS_REQUIRED = MAX_MARKCOUNT_BITS;
065  public static final int GLOBAL_GC_BITS_REQUIRED = 0;
066  public static final int GC_HEADER_WORDS_REQUIRED = 0;
067
068
069  /****************************************************************************
070   *
071   * Instance variables
072   */
073
074  /**
075   *
076   */
077  private byte markState = 1;
078  private byte allocState = 0;
079  private boolean inMSCollection;
080  private static final boolean usingStickyMarkBits = VM.activePlan.constraints().needsLogBitInHeader(); /* are sticky mark bits in use? */
081  private boolean isAgeSegregated = false; /* is this space a nursery space? */
082  private boolean isAllocAsMarked = false;
083
084  /****************************************************************************
085   *
086   * Initialization
087   */
088
089  static {
090    Options.markSweepMarkBits = new MarkSweepMarkBits();
091    Options.eagerCompleteSweep = new EagerCompleteSweep();
092  }
093
094  /**
095   * The caller specifies the region of virtual memory to be used for
096   * this space.  If this region conflicts with an existing space,
097   * then the constructor will fail.
098   *
099   * @param name The name of this space (used when printing error messages etc)
100   * @param vmRequest An object describing the virtual memory requested.
101   */
102  public MarkSweepSpace(String name, VMRequest vmRequest) {
103    super(name, 0, vmRequest);
104    if (usingStickyMarkBits) allocState |= HeaderByte.UNLOGGED_BIT;
105  }
106
107  /**
108   * This instance will be age-segregated using the sticky mark bits
109   * algorithm. Perform appropriate initialization
110   */
111  public void makeAgeSegregatedSpace() {
112    /* we must be using sticky mark bits */
113    if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(usingStickyMarkBits);
114    allocState &= ~HeaderByte.UNLOGGED_BIT; /* clear the unlogged bit for nursery allocs */
115    isAgeSegregated = true;
116  }
117
118  /**
119   * Should SegregatedFreeListSpace manage a side bitmap to keep track of live objects?
120   */
121  @Override
122  @Inline
123  protected boolean maintainSideBitmap() {
124    return !HEADER_MARK_BITS;
125  }
126
127  @Override
128  @Inline
129  protected boolean preserveFreeList() {
130    return !LAZY_SWEEP;
131  }
132
133  /****************************************************************************
134   *
135   * Allocation
136   */
137
138  /**
139   * Prepare the next block in the free block list for use by the free
140   * list allocator.  In the case of lazy sweeping this involves
141   * sweeping the available cells.  <b>The sweeping operation must
142   * ensure that cells are pre-zeroed</b>, as this method must return
143   * pre-zeroed cells.
144   *
145   * @param block The block to be prepared for use
146   * @param sizeClass The size class of the block
147   * @return The address of the first pre-zeroed cell in the free list
148   * for this block, or zero if there are no available cells.
149   */
150  @Override
151  protected Address advanceToBlock(Address block, int sizeClass) {
152    if (HEADER_MARK_BITS) {
153      if (inMSCollection) markBlock(block);
154    }
155
156    if (LAZY_SWEEP) {
157      return makeFreeList(block, sizeClass);
158    } else {
159      return getFreeList(block);
160    }
161  }
162
163  /**
164   * {@inheritDoc}<p>
165   *
166   * This is to ensure that appropriate collection state can be initialized
167   * for the block.
168   */
169  @Override
170  protected void notifyNewBlock(Address block, int sizeClass) {
171    if (HEADER_MARK_BITS) {
172      if (inMSCollection) markBlock(block);
173    }
174  }
175
176  /****************************************************************************
177   *
178   * Collection
179   */
180
181  /**
182   * Prepare for a new collection increment.  For the mark-sweep
183   * collector we must flip the state of the mark bit between
184   * collections.
185   *
186   * @param gcWholeMS True if we are going to collect the whole marksweep space
187   */
188  public void prepare(boolean gcWholeMS) {
189    if (HEADER_MARK_BITS && Options.eagerCompleteSweep.getValue()) {
190      consumeBlocks();
191    } else {
192      flushAvailableBlocks();
193    }
194    if (HEADER_MARK_BITS) {
195      if (gcWholeMS) {
196        allocState = markState;
197        if (usingStickyMarkBits && !isAgeSegregated) /* if true, we allocate as "mature", not nursery */
198          allocState |= HeaderByte.UNLOGGED_BIT;
199        markState = deltaMarkState(true);
200        if (EAGER_MARK_CLEAR)
201          clearAllBlockMarks();
202      }
203    } else {
204      zeroLiveBits();
205    }
206    inMSCollection = true;
207  }
208
209  /**
210   * A new collection increment has completed.  For the mark-sweep
211   * collector this means we can perform the sweep phase.
212 */
213  public void release() {
214    sweepConsumedBlocks(!EAGER_MARK_CLEAR);
215    inMSCollection = false;
216  }
217
218  /**
219   * Release an allocated page or pages
220   *
221   * @param start The address of the start of the page or pages
222   */
223  @Override
224  @Inline
225  public void release(Address start) {
226    ((FreeListPageResource) pr).releasePages(start);
227  }
228
229  /**
230   * Should the sweep reclaim the cell containing this object. Is this object
231   * live. This is only used when maintainSideBitmap is false.
232   *
233   * @param object The object to query
234   * @return True if the cell should be reclaimed
235   */
236  @Override
237  @Inline
238  protected boolean isCellLive(ObjectReference object) {
239    if (!HEADER_MARK_BITS) {
240      return super.isCellLive(object);
241    }
242    return testMarkState(object);
243  }
244
245  /****************************************************************************
246   *
247   * Object processing and tracing
248   */
249
250  /**
251   * Trace a reference to an object under a mark sweep collection
252   * policy.  If the object header is not already marked, mark the
253   * object in either the bitmap or by moving it off the treadmill,
254   * and enqueue the object for subsequent processing. The object is
255   * marked as (an atomic) side-effect of checking whether already
256   * marked.
257   *
258   * @param object The object to be traced.
259   * @return The object (there is no object forwarding in this
260   * collector, so we always return the same object: this could be a
261   * void method but for compliance to a more general interface).
262   */
263  @Override
264  @Inline
265  public ObjectReference traceObject(TransitiveClosure trace, ObjectReference object) {
266    if (HEADER_MARK_BITS) {
267      if (testAndMark(object)) {
268        markBlock(object);
269        trace.processNode(object);
270      }
271    } else {
272      if (testAndSetLiveBit(object)) {
273        trace.processNode(object);
274      }
275    }
276    return object;
277  }
278
279  /**
280   * @return {@code true} if this object is known to be live (i.e. it is marked)
281   */
282  @Override
283  @Inline
284  public boolean isLive(ObjectReference object) {
285    if (HEADER_MARK_BITS) {
286      return testMarkState(object);
287    } else {
288      return liveBitSet(object);
289    }
290  }
291
292  /**
293   * Get the previous mark state.
294   *
295   * @return The previous mark state.
296   */
297  @Inline
298  public byte getPreviousMarkState() {
299    return deltaMarkState(false);
300  }
301
302  /**
303   * Return the mark state incremented or decremented by one.
304   *
305   * @param increment If true, then return the incremented value else return the decremented value
306   * @return the mark state incremented or decremented by one.
307   */
308  private byte deltaMarkState(boolean increment) {
309    byte mask = (byte) (((1 << Options.markSweepMarkBits.getValue()) - 1) << COUNT_BASE);
310    byte rtn = (byte) (increment ? markState + MARK_COUNT_INCREMENT : markState - MARK_COUNT_INCREMENT);
311    rtn &= mask;
312    if (VM.VERIFY_ASSERTIONS) VM.assertions._assert((markState & ~MARK_COUNT_MASK) == 0);
313    return rtn;
314  }
315
316  /****************************************************************************
317   *
318   * Header manipulation
319   */
320
321  /**
322   * Perform any required post allocation initialization
323   *
324   * @param object the object ref to the storage to be initialized
325   */
326  @Inline
327  public void postAlloc(ObjectReference object) {
328    initializeHeader(object, true);
329  }
330
331  /**
332   * Perform any required post copy (i.e. in-GC allocation) initialization.
333   * This is relevant (for example) when MS is used as the mature space in
334   * a copying GC.
335   *
336   * @param object the object ref to the storage to be initialized
337   * @param majorGC Is this copy happening during a major gc?
338   */
339  @Inline
340  public void postCopy(ObjectReference object, boolean majorGC) {
341    initializeHeader(object, false);
342    if (!HEADER_MARK_BITS) {
343      testAndSetLiveBit(object);
344    }
345  }
346
347  /**
348   * Perform any required initialization of the GC portion of the header.
349   *
350   * @param object the object ref to the storage to be initialized
351   * @param alloc is this initialization occuring due to (initial) allocation
352   * (true) or due to copying (false)?
353   */
354  @Inline
355  public void initializeHeader(ObjectReference object, boolean alloc) {
356    if (HEADER_MARK_BITS) {
357      byte oldValue = VM.objectModel.readAvailableByte(object);
358      byte newValue = (byte) ((oldValue & ~MARK_COUNT_MASK) | (alloc && !isAllocAsMarked ? allocState : markState));
359      VM.objectModel.writeAvailableByte(object, newValue);
360    } else if (HeaderByte.NEEDS_UNLOGGED_BIT)
361      HeaderByte.markAsUnlogged(object);
362  }
363
364  /**
365   * Atomically attempt to set the mark bit of an object.
366   *
367   * @param object The object whose mark bit is to be set
368   * @return {@code true} if successful, {@code false} if the mark
369   *  bit was already set
370   */
371  @Inline
372  private boolean testAndMark(ObjectReference object) {
373    byte oldValue, markBits, newValue;
374    oldValue = VM.objectModel.readAvailableByte(object);
375    markBits = (byte) (oldValue & MARK_COUNT_MASK);
376    if (markBits == markState) return false;
377    newValue = (byte)((oldValue & ~MARK_COUNT_MASK) | markState);
378    if (HeaderByte.NEEDS_UNLOGGED_BIT) newValue |= HeaderByte.UNLOGGED_BIT;
379    VM.objectModel.writeAvailableByte(object, newValue);
380    return true;
381  }
382
383  /**
384   * Return true if the mark count for an object has the given value.
385   *
386   * @param object The object whose mark bit is to be tested
387   * @return <code>true</code> if the mark bit for the object is set.
388   */
389  @Inline
390  private boolean testMarkState(ObjectReference object) {
391    if (VM.VERIFY_ASSERTIONS) VM.assertions._assert((markState & ~MARK_COUNT_MASK) == 0);
392    return (VM.objectModel.readAvailableByte(object) & MARK_COUNT_MASK) == markState;
393  }
394
395  public void makeAllocAsMarked() {
396    isAllocAsMarked = true;
397  }
398}