blob: 665854c4f9effc12e18df50a3d83d03079a715a0 [file] [log] [blame]
/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.mmtk.policy;
import org.mmtk.plan.TransitiveClosure;
import org.mmtk.utility.heap.FreeListPageResource;
import org.mmtk.utility.heap.VMRequest;
import org.mmtk.utility.HeaderByte;
import org.mmtk.utility.Treadmill;
import org.mmtk.vm.VM;
import org.vmmagic.pragma.*;
import org.vmmagic.unboxed.*;
/**
* Each instance of this class corresponds to one explicitly managed
* large object space.
*/
@Uninterruptible
public final class LargeObjectSpace extends BaseLargeObjectSpace {
/****************************************************************************
*
* Class variables
*/
public static final int LOCAL_GC_BITS_REQUIRED = 2;
public static final int GLOBAL_GC_BITS_REQUIRED = 0;
private static final byte MARK_BIT = 1; // ...01
private static final byte NURSERY_BIT = 2; // ...10
private static final byte LOS_BIT_MASK = 3; // ...11
/****************************************************************************
*
* Instance variables
*/
private byte markState;
private boolean inNurseryGC;
private final Treadmill treadmill;
/****************************************************************************
*
* Initialization
*/
/**
* The caller specifies the region of virtual memory to be used for
* this space. If this region conflicts with an existing space,
* then the constructor will fail.
*
* @param name The name of this space (used when printing error messages etc)
* @param pageBudget The number of pages this space may consume
* before consulting the plan
* @param vmRequest An object describing the virtual memory requested.
*/
public LargeObjectSpace(String name, int pageBudget, VMRequest vmRequest) {
super(name, pageBudget, vmRequest);
treadmill = new Treadmill(LOG_BYTES_IN_PAGE, true);
markState = 0;
}
/****************************************************************************
*
* Collection
*/
/**
* Prepare for a new collection increment. For the mark-sweep
* collector we must flip the state of the mark bit between
* collections.
*/
public void prepare(boolean fullHeap) {
if (fullHeap) {
if (VM.VERIFY_ASSERTIONS) {
VM.assertions._assert(treadmill.fromSpaceEmpty());
}
markState = (byte) (MARK_BIT - markState);
}
treadmill.flip(fullHeap);
inNurseryGC = !fullHeap;
}
/**
* A new collection increment has completed. For the mark-sweep
* collector this means we can perform the sweep phase.
*/
public void release(boolean fullHeap) {
// sweep the large objects
sweepLargePages(true); // sweep the nursery
if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(treadmill.nurseryEmpty());
if (fullHeap) sweepLargePages(false); // sweep the mature space
}
/**
* Sweep through the large pages, releasing all superpages on the
* "from space" treadmill.
*/
private void sweepLargePages(boolean sweepNursery) {
while (true) {
Address cell = sweepNursery ? treadmill.popNursery() : treadmill.pop();
if (cell.isZero()) break;
release(getSuperPage(cell));
}
if (VM.VERIFY_ASSERTIONS) VM.assertions._assert(sweepNursery ? treadmill.nurseryEmpty() : treadmill.fromSpaceEmpty());
}
/**
* Release a group of pages that were allocated together.
*
* @param first The first page in the group of pages that were
* allocated together.
*/
@Inline
public void release(Address first) {
((FreeListPageResource) pr).releasePages(first);
}
/****************************************************************************
*
* Object processing and tracing
*/
/**
* Trace a reference to an object under a mark sweep collection
* policy. If the object header is not already marked, mark the
* object in either the bitmap or by moving it off the treadmill,
* and enqueue the object for subsequent processing. The object is
* marked as (an atomic) side-effect of checking whether already
* marked.
*
* @param trace The trace being conducted.
* @param object The object to be traced.
* @return The object (there is no object forwarding in this
* collector, so we always return the same object: this could be a
* void method but for compliance to a more general interface).
*/
@Inline
public ObjectReference traceObject(TransitiveClosure trace, ObjectReference object) {
boolean nurseryObject = isInNursery(object);
if (!inNurseryGC || nurseryObject) {
if (testAndMark(object, markState)) {
internalMarkObject(object, nurseryObject);
trace.processNode(object);
}
}
return object;
}
/**
* @param object The object in question
* @return True if this object is known to be live (i.e. it is marked)
*/
@Inline
public boolean isLive(ObjectReference object) {
return testMarkBit(object, markState);
}
/**
* An object has been marked (identifiged as live). Large objects
* are added to the to-space treadmill, while all other objects will
* have a mark bit set in the superpage header.
*
* @param object The object which has been marked.
*/
@Inline
private void internalMarkObject(ObjectReference object, boolean nurseryObject) {
Address cell = VM.objectModel.objectStartRef(object);
Address node = Treadmill.midPayloadToNode(cell);
treadmill.copy(node, nurseryObject);
}
/****************************************************************************
*
* Header manipulation
*/
/**
* Perform any required initialization of the GC portion of the header.
*
* @param object the object ref to the storage to be initialized
* @param alloc is this initialization occuring due to (initial) allocation
* (true) or due to copying (false)?
*/
@Inline
public void initializeHeader(ObjectReference object, boolean alloc) {
byte oldValue = VM.objectModel.readAvailableByte(object);
byte newValue = (byte) ((oldValue & ~LOS_BIT_MASK) | markState);
if (alloc) newValue |= NURSERY_BIT;
if (HeaderByte.NEEDS_UNLOGGED_BIT) newValue |= HeaderByte.UNLOGGED_BIT;
VM.objectModel.writeAvailableByte(object, newValue);
Address cell = VM.objectModel.objectStartRef(object);
treadmill.addToTreadmill(Treadmill.midPayloadToNode(cell), alloc);
}
/**
* Atomically attempt to set the mark bit of an object. Return true
* if successful, false if the mark bit was already set.
*
* @param object The object whose mark bit is to be written
* @param value The value to which the mark bit will be set
*/
@Inline
private boolean testAndMark(ObjectReference object, byte value) {
Word oldValue;
do {
oldValue = VM.objectModel.prepareAvailableBits(object);
byte markBit = (byte) (oldValue.toInt() & (inNurseryGC ? LOS_BIT_MASK : MARK_BIT));
if (markBit == value) return false;
} while (!VM.objectModel.attemptAvailableBits(object, oldValue,
oldValue.and(Word.fromIntZeroExtend(LOS_BIT_MASK).not()).or(Word.fromIntZeroExtend(value))));
return true;
}
/**
* Return true if the mark bit for an object has the given value.
*
* @param object The object whose mark bit is to be tested
* @param value The value against which the mark bit will be tested
* @return True if the mark bit for the object has the given value.
*/
@Inline
private boolean testMarkBit(ObjectReference object, byte value) {
return (byte) (VM.objectModel.readAvailableByte(object) & MARK_BIT) == value;
}
/**
* Return true if the object is in the logical nursery
*
* @param object The object whose status is to be tested
* @return True if the object is in the logical nursery
*/
@Inline
private boolean isInNursery(ObjectReference object) {
return (byte)(VM.objectModel.readAvailableByte(object) & NURSERY_BIT) == NURSERY_BIT;
}
/**
* Return the size of the per-superpage header required by this
* system. In this case it is just the underlying superpage header
* size.
*
* @return The size of the per-superpage header required by this
* system.
*/
@Inline
protected int superPageHeaderSize() {
return Treadmill.headerSize();
}
/**
* Return the size of the per-cell header for cells of a given class
* size.
*
* @return The size of the per-cell header for cells of a given class
* size.
*/
@Inline
protected int cellHeaderSize() {
return 0;
}
/**
* This is the treadmill used by the large object space.
*
* Note that it depends on the specific local in use whether this
* is being used.
*
* @return The treadmill associated with this large object space.
*/
public Treadmill getTreadmill() {
return this.treadmill;
}
}