/*
* Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.max.vm.heap.gcx;
import static com.sun.max.vm.heap.gcx.HeapRegionConstants.*;
import com.sun.max.annotate.*;
import com.sun.max.unsafe.*;
import com.sun.max.vm.*;
import com.sun.max.vm.MaxineVM.Phase;
import com.sun.max.vm.heap.gcx.rset.*;
import com.sun.max.vm.layout.*;
import com.sun.max.vm.runtime.*;
/**
* Sweeping interface for heap made of multiple, possibly discontinuous regions.
* A heap marker interacts with the sweeper via a hasNextSweepingRegion / beginSweep / endSweep methods that bracket the sweeping of each region.
* Dead space within the region is notified to the sweeper via three interface: processLargeGap, processDeadSpace, and processFreeRegion.
*/
public abstract class HeapRegionSweeper extends Sweeper {
static int SweepBreakAtRegion = -1;
static {
VMOptions.addFieldOption("-XX:", "SweepBreakAtRegion", HeapRegionSweeper.class, "Break before sweeping region", Phase.PRISTINE);
}
protected Size minReclaimableSpace;
/**
* Region information for the current sweeping region (csr).
*/
HeapRegionInfo csrInfo;
/**
* Number of free bytes recorded for the csr.
*/
int csrFreeBytes;
/**
* Number of live bytes recorded for the csr.
*/
int csrLiveBytes;
/**
* Number of free chunks in the csr.
*/
int csrFreeChunks;
/**
* Head of the list of free chunks recorded for the csr.
*/
HeapFreeChunk csrHead;
/**
* Tail of the list of free chunks recorded for the csr.
*/
HeapFreeChunk csrTail;
/**
* End of the current sweeping region.
*/
Address csrEnd;
/**
* Indicate that the region is the head of a multi-regions object.
*/
boolean csrIsMultiRegionObjectHead;
/**
* Indicate that the sweeping region is the tail of a live multi-regions object.
*/
boolean csrIsLiveMultiRegionObjectTail;
/**
* Cursor on the last live address seen during sweeping of the csr.
*/
Address csrLastLiveAddress;
/**
* True if all dead spaces require their references to be erased. This is required if an imprecise remembered set (e.g., card table) is used for
* root tracing. Erasing dead references is equivalent to turning dead space into reference-less heap cell.
*/
final boolean zapDeadReferences;
/**
* Action to performed on a remembered set when dead space is identified.
*/
final DeadSpaceListener deadSpaceListener;
boolean traceGap;
protected HeapRegionSweeper(boolean zapDeadReferences, DeadSpaceListener deadSpaceListener) {
this.zapDeadReferences = zapDeadReferences;
this.deadSpaceListener = deadSpaceListener;
}
final public int liveBytes() {
return csrLiveBytes;
}
@Override
public final Size minReclaimableSpace() {
return minReclaimableSpace;
}
@Override
public final Address startOfSweepingRegion() {
return csrLastLiveAddress;
}
@Override
public final Address endOfSweepingRegion() {
return csrEnd;
}
@NEVER_INLINE
private void breakpoint() {
}
final void resetSweepingRegion(HeapRegionInfo rinfo) {
if (MaxineVM.isDebug() && SweepBreakAtRegion >= 0 && SweepBreakAtRegion == rinfo.toRegionID()) {
breakpoint();
}
final Address regionStart = rinfo.regionStart();
csrInfo = rinfo;
csrEnd = regionStart.plus(regionSizeInBytes);
csrFreeBytes = 0;
csrHead = null;
csrTail = null;
csrFreeChunks = 0;
csrLiveBytes = 0;
if (!csrIsLiveMultiRegionObjectTail) {
csrIsMultiRegionObjectHead = csrInfo.isHeadOfLargeObject();
csrLastLiveAddress = regionStart;
} else if (MaxineVM.isDebug()) {
// Otherwise, csrLastLiveAddress is the address of the last word of the live multi-region object.
FatalError.check(csrLastLiveAddress.greaterEqual(regionStart) && csrLastLiveAddress.lessEqual(csrEnd), "csrLastLiveAddress must be within tail region");
}
HeapRegionState.EMPTY_REGION.setState(csrInfo);
csrInfo.resetOccupancy();
}
final void recordFreeSpace(Address chunk, Size chunkSize) {
HeapFreeChunk c = HeapFreeChunk.format(chunk, chunkSize);
if (csrTail == null) {
csrHead = c;
} else {
csrTail.next = c;
}
csrTail = c;
csrFreeChunks++;
csrFreeBytes += chunkSize.toInt();
}
private void recordIfReclaimable(Address address, Size size) {
if (size.greaterEqual(minReclaimableSpace)) {
if (MaxineVM.isDebug()) {
logger.logFreeSpace(address, size);
}
recordFreeSpace(address, size);
} else {
if (zapDeadReferences) {
DarkMatter.format(address, size);
}
if (MaxineVM.isDebug()) {
logger.logDeadSpace(address, size);
}
}
}
/**
* Process a region without any live mark. If the region isn't part of a multi-region object, then it is free.
* Otherwise, if it is the tail of a multi-region object, we need to record a single chunk after its tail.
*/
public void processDeadRegion() {
if (csrIsLiveMultiRegionObjectTail) {
Size tailSize = csrEnd.minus(csrLastLiveAddress).asSize();
if (MaxineVM.isDebug()) {
FatalError.check(csrLastLiveAddress.greaterThan(csrInfo.regionStart()),
"Last live address must be greater than start of a live multi-regions object's tail");
}
recordIfReclaimable(csrLastLiveAddress, tailSize);
} else {
if (MaxineVM.isDebug()) {
logger.logFreeSpace(csrInfo.regionStart(), Size.fromInt(regionSizeInBytes));
}
csrFreeBytes = regionSizeInBytes;
}
}
public abstract boolean hasNextSweepingRegion();
public abstract void reachedRightmostLiveRegion();
/**
* Indicates whether the current sweeping region is the head of a multi-regions object.
*/
public final boolean sweepingRegionIsLargeHead() {
return csrIsMultiRegionObjectHead;
}
@Override
public abstract void beginSweep();
@Override
public abstract void endSweep();
@Override
public abstract void verify(AfterMarkSweepVerifier verifier);
/**
* Invoked when doing imprecise sweeping to process a large interval between two marked locations.
* Imprecise heap sweeping ignores any space before two live objects smaller than a specified amount of space.
* When the distance between two live marks is large enough to indicate a potentially large chunk of free space,
* the sweeper invoke this method. Note however that the reported gap may not be entirely free
* (e.g., if the first object of the gap is actually larger than the minimum gap size).
* FIXME: probably better to just have a processDeadSpace interface and leave these details to the imprecise sweeper.
*
* @param leftLiveObject
* @param rightLiveObject
*/
@Override
public Pointer processLargeGap(Pointer leftLiveObject, Pointer rightLiveObject) {
FatalError.check(rightLiveObject.lessEqual(endOfSweepingRegion()), "dead space must not cross region boundary");
Pointer endOfLeftObject = leftLiveObject.plus(Layout.size(Layout.cellToOrigin(leftLiveObject)));
csrLiveBytes += endOfLeftObject.minus(csrLastLiveAddress).asSize().toInt();
if (MaxineVM.isDebug()) {
logger.logGap(leftLiveObject, rightLiveObject);
}
Size numDeadBytes = rightLiveObject.minus(endOfLeftObject).asSize();
recordIfReclaimable(endOfLeftObject, numDeadBytes);
csrLastLiveAddress = rightLiveObject.plus(Layout.size(Layout.cellToOrigin(rightLiveObject)));
return csrLastLiveAddress.asPointer();
}
/**
* Invoked to record a known chunk of free space.
* Used both by precise and imprecise sweeper, typically to record the unmarked space
* at both end of the traced space.
* @param freeChunk
* @param size
*/
@Override
public void processDeadSpace(Address freeChunk, Size size) {
assert freeChunk.plus(size).lessEqual(endOfSweepingRegion());
csrLastLiveAddress = freeChunk.plus(size);
if (MaxineVM.isDebug()) {
logger.logFreeSpace(freeChunk, size);
}
recordFreeSpace(freeChunk, size);
}
@Override
public Pointer processLiveObject(Pointer liveObject) {
final Size numDeadBytes = liveObject.minus(csrLastLiveAddress).asSize();
if (!numDeadBytes.isZero()) {
final Pointer deadSpace = csrLastLiveAddress.asPointer();
recordIfReclaimable(deadSpace, numDeadBytes);
deadSpaceListener.notifyCoalescing(deadSpace, numDeadBytes);
}
final Size numLiveBytes = Layout.size(Layout.cellToOrigin(liveObject));
csrLastLiveAddress = liveObject.plus(numLiveBytes);
csrLiveBytes += numLiveBytes.toInt();
return csrLastLiveAddress.asPointer();
}
}