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