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 static org.mmtk.utility.Constants.LOG_BYTES_IN_PAGE;
016
017import org.mmtk.plan.TransitiveClosure;
018import org.mmtk.utility.heap.FreeListPageResource;
019import org.mmtk.utility.heap.VMRequest;
020import org.mmtk.utility.HeaderByte;
021import org.mmtk.utility.Treadmill;
022
023import org.mmtk.vm.VM;
024
025import org.vmmagic.pragma.*;
026import org.vmmagic.unboxed.*;
027
028/**
029 * Each instance of this class corresponds to one explicitly managed
030 * large object space.
031 */
032@Uninterruptible
033public final class LargeObjectSpace extends BaseLargeObjectSpace {
034
035  /****************************************************************************
036   *
037   * Class variables
038   */
039
040  /**
041   *
042   */
043  public static final int LOCAL_GC_BITS_REQUIRED = 2;
044  public static final int GLOBAL_GC_BITS_REQUIRED = 0;
045  private static final byte MARK_BIT =     1; // ...01
046  private static final byte NURSERY_BIT =  2; // ...10
047  private static final byte LOS_BIT_MASK = 3; // ...11
048
049  /****************************************************************************
050   *
051   * Instance variables
052   */
053
054  /**
055   *
056   */
057  private byte markState;
058  private boolean inNurseryGC;
059  private final Treadmill treadmill;
060
061  /****************************************************************************
062   *
063   * Initialization
064   */
065
066  /**
067   * The caller specifies the region of virtual memory to be used for
068   * this space.  If this region conflicts with an existing space,
069   * then the constructor will fail.
070   *
071   * @param name The name of this space (used when printing error messages etc)
072   * @param vmRequest An object describing the virtual memory requested.
073   */
074  public LargeObjectSpace(String name, VMRequest vmRequest) {
075    this(name, true, vmRequest);
076  }
077
078  /**
079   * The caller specifies the region of virtual memory to be used for
080   * this space.  If this region conflicts with an existing space,
081   * then the constructor will fail.
082   *
083   * @param name The name of this space (used when printing error messages etc)
084   * @param zeroed if true, allocations return zeroed memory.
085   * @param vmRequest An object describing the virtual memory requested.
086   */
087  public LargeObjectSpace(String name, boolean zeroed, VMRequest vmRequest) {
088    super(name, zeroed, vmRequest);
089    treadmill = new Treadmill(LOG_BYTES_IN_PAGE, true);
090    markState = 0;
091  }
092
093  /****************************************************************************
094   *
095   * Collection
096   */
097
098  /**
099   * Prepares for a new collection increment.  For the mark-sweep
100   * collector we must flip the state of the mark bit between
101   * collections.
102   *
103   * @param fullHeap whether the collection will be full heap
104   */
105  public void prepare(boolean fullHeap) {
106    if (fullHeap) {
107      if (VM.VERIFY_ASSERTIONS) {
108        VM.assertions._assert(treadmill.fromSpaceEmpty());
109      }
110      markState = (byte) (MARK_BIT - markState);
111    }
112    treadmill.flip(fullHeap);
113    inNurseryGC = !fullHeap;
114  }
115
116  /**
117   * A new collection increment has completed.  For the mark-sweep
118   * collector this means we can perform the sweep phase.
119   *
120   * @param fullHeap whether the collection was full heap
121   */
122  public void release(boolean fullHeap) {
123    // sweep the large objects
124    sweepLargePages(true);                // sweep the nursery
125    if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(treadmill.nurseryEmpty());
126    if (fullHeap) sweepLargePages(false); // sweep the mature space
127  }
128
129  /**
130   * Sweeps through the large pages, releasing all superpages on the
131   * "from space" treadmill.
132   *
133   * @param sweepNursery whether to sweep the nursery
134   */
135  private void sweepLargePages(boolean sweepNursery) {
136    while (true) {
137      Address cell = sweepNursery ? treadmill.popNursery() : treadmill.pop();
138      if (cell.isZero()) break;
139      release(getSuperPage(cell));
140    }
141    if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(sweepNursery ? treadmill.nurseryEmpty() : treadmill.fromSpaceEmpty());
142  }
143
144  @Override
145  @Inline
146  public void release(Address first) {
147    ((FreeListPageResource) pr).releasePages(first);
148  }
149
150  /****************************************************************************
151   *
152   * Object processing and tracing
153   */
154
155  /**
156   * Trace a reference to an object under a mark sweep collection
157   * policy.  If the object header is not already marked, mark the
158   * object in either the bitmap or by moving it off the treadmill,
159   * and enqueue the object for subsequent processing. The object is
160   * marked as (an atomic) side-effect of checking whether already
161   * marked.
162   *
163   * @param trace The trace being conducted.
164   * @param object The object to be traced.
165   * @return The object (there is no object forwarding in this
166   * collector, so we always return the same object: this could be a
167   * void method but for compliance to a more general interface).
168   */
169  @Override
170  @Inline
171  public ObjectReference traceObject(TransitiveClosure trace, ObjectReference object) {
172    boolean nurseryObject = isInNursery(object);
173    if (!inNurseryGC || nurseryObject) {
174      if (testAndMark(object, markState)) {
175        internalMarkObject(object, nurseryObject);
176        trace.processNode(object);
177      }
178    }
179    return object;
180  }
181
182  /**
183   * @param object The object in question
184   * @return {@code true} if this object is known to be live (i.e. it is marked)
185   */
186  @Override
187  @Inline
188   public boolean isLive(ObjectReference object) {
189    return testMarkBit(object, markState);
190  }
191
192  /**
193   * An object has been marked (identifiged as live).  Large objects
194   * are added to the to-space treadmill, while all other objects will
195   * have a mark bit set in the superpage header.
196   *
197   * @param object The object which has been marked.
198   * @param nurseryObject whether the object is in the nursery
199   */
200  @Inline
201  private void internalMarkObject(ObjectReference object, boolean nurseryObject) {
202
203    Address cell = VM.objectModel.objectStartRef(object);
204    Address node = Treadmill.midPayloadToNode(cell);
205    treadmill.copy(node, nurseryObject);
206  }
207
208  /****************************************************************************
209   *
210   * Header manipulation
211   */
212
213  /**
214   * Perform any required initialization of the GC portion of the header.
215   *
216   * @param object the object ref to the storage to be initialized
217   * @param alloc is this initialization occurring due to (initial) allocation
218   * ({@code true}) or due to copying ({@code false})?
219   */
220  @Inline
221  public void initializeHeader(ObjectReference object, boolean alloc) {
222    byte oldValue = VM.objectModel.readAvailableByte(object);
223    byte newValue = (byte) ((oldValue & ~LOS_BIT_MASK) | markState);
224    if (alloc) newValue |= NURSERY_BIT;
225    if (HeaderByte.NEEDS_UNLOGGED_BIT) newValue |= HeaderByte.UNLOGGED_BIT;
226    VM.objectModel.writeAvailableByte(object, newValue);
227    Address cell = VM.objectModel.objectStartRef(object);
228    treadmill.addToTreadmill(Treadmill.midPayloadToNode(cell), alloc);
229  }
230
231  /**
232   * Atomically attempt to set the mark bit of an object.
233   *
234   * @param object The object whose mark bit is to be written
235   * @param value The value to which the mark bit will be set
236   * @return {@code true} if successful, {@code false} if the
237   *  mark bit was already set.
238   */
239  @Inline
240  private boolean testAndMark(ObjectReference object, byte value) {
241    Word oldValue;
242    do {
243      oldValue = VM.objectModel.prepareAvailableBits(object);
244      byte markBit = (byte) (oldValue.toInt() & (inNurseryGC ? LOS_BIT_MASK : MARK_BIT));
245      if (markBit == value) return false;
246    } while (!VM.objectModel.attemptAvailableBits(object, oldValue,
247                                                  oldValue.and(Word.fromIntZeroExtend(LOS_BIT_MASK).not()).or(Word.fromIntZeroExtend(value))));
248    return true;
249  }
250
251  /**
252   * Return {@code true} if the mark bit for an object has the given value.
253   *
254   * @param object The object whose mark bit is to be tested
255   * @param value The value against which the mark bit will be tested
256   * @return {@code true} if the mark bit for the object has the given value.
257   */
258  @Inline
259  private boolean testMarkBit(ObjectReference object, byte value) {
260    return (byte) (VM.objectModel.readAvailableByte(object) & MARK_BIT) == value;
261  }
262
263  /**
264   * Return {@code true} if the object is in the logical nursery
265   *
266   * @param object The object whose status is to be tested
267   * @return {@code true} if the object is in the logical nursery
268   */
269  @Inline
270  private boolean isInNursery(ObjectReference object) {
271     return (byte)(VM.objectModel.readAvailableByte(object) & NURSERY_BIT) == NURSERY_BIT;
272  }
273
274  @Override
275  @Inline
276  protected int superPageHeaderSize() {
277    return Treadmill.headerSize();
278  }
279
280  @Override
281  @Inline
282  protected int cellHeaderSize() {
283    return 0;
284  }
285
286  /**
287   * This is the treadmill used by the large object space.
288   *
289   * Note that it depends on the specific local in use whether this
290   * is being used.
291   *
292   * @return The treadmill associated with this large object space.
293   */
294  public Treadmill getTreadmill() {
295    return this.treadmill;
296  }
297}