/*
* Copyright (c) 2010, 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.intrinsics.MaxineIntrinsicIDs.*;
import com.sun.max.annotate.*;
import com.sun.max.memory.*;
import com.sun.max.unsafe.*;
import com.sun.max.vm.*;
import com.sun.max.vm.actor.holder.*;
import com.sun.max.vm.heap.*;
import com.sun.max.vm.layout.*;
import com.sun.max.vm.reference.*;
import com.sun.max.vm.runtime.*;
import com.sun.max.vm.type.*;
/**
* A chunk of free space in the heap.
* Sweepers format re-usable free space as HeapFreeChunk and Long arrays.
* This ease manipulation by space allocator, inspection of the heap, debugging and
* heap walking.
* Reference to HeapFreeChunk must never be stored in object and must never be used
* when safepoint is enabled, otherwise they become visible to GC and will be considered live.
* Similarly, direct updates to HeapFreeChunk.next may cause unwanted write-barrier executions.
*
* TODO (ld) need to revisit the visibility of this class.
*/
public class HeapFreeChunk {
@FOLD
public static final DynamicHub heapFreeChunkHub() {
return ClassActor.fromJava(HeapFreeChunk.class).dynamicHub();
}
@FOLD
public static Size heapFreeChunkHeaderSize() {
return HeapFreeChunk.heapFreeChunkHub().tupleSize;
}
/**
* Index of the word storing "next" field of the heap free chunk.
*/
@FOLD
protected static int nextIndex() {
return ClassRegistry.findField(HeapFreeChunk.class, "next").offset() >> Word.widthValue().log2numberOfBytes;
}
/**
* Index of the word storing "size" field of the heap free chunk.
*/
@FOLD
protected static int sizeIndex() {
return ClassRegistry.findField(HeapFreeChunk.class, "size").offset() >> Word.widthValue().log2numberOfBytes;
}
@INLINE
public static boolean isInDeadSpace(Address chunkAddress) {
return chunkAddress.wordAligned().asPointer().getLong() == Memory.ZAPPED_MARKER;
}
@INLINE
public static boolean isDeadSpaceMark(Address a) {
return a.equals(deadSpaceMark());
}
@FOLD
public static Address deadSpaceMark() {
return Memory.zappedMarker();
}
@INLINE
public static Address getFreeChunkNext(Address chunkAddress) {
return chunkAddress.asPointer().getWord(nextIndex()).asAddress();
}
@INLINE
public static Size getFreechunkSize(Address chunkAddress) {
return chunkAddress.asPointer().getWord(sizeIndex()).asSize();
}
@INLINE
public static void setFreeChunkNext(Address chunkAddress, Address nextChunkAddress) {
chunkAddress.asPointer().setWord(nextIndex(), nextChunkAddress);
}
@INLINE
public static void setFreeChunkSize(Address chunkAddress, Size size) {
chunkAddress.asPointer().setWord(sizeIndex(), size);
}
public static boolean isHeapFreeChunkOrigin(Pointer origin) {
return Reference.fromJava(heapFreeChunkHub()).toOrigin().equals(origin.readWord(Layout.hubIndex()));
}
/**
* Return true if the specified area is formated as a tail chunk (i.e., chunk with a null next field).
* @param start start of the heap area
* @param end end of the heap area
*/
public static boolean isTailFreeChunk(Pointer start, Pointer end) {
if (end.lessThan(start.plus(heapFreeChunkHeaderSize()))) {
return false;
}
final Pointer origin = Layout.cellToOrigin(start);
// First, do we have a HEAP_FREE_CHUNK_HUB at the location corresponding to a cell's hub:
if (!isHeapFreeChunkOrigin(origin)) {
return false;
}
// Next free should be null
if (!getFreeChunkNext(start).isZero()) {
return false;
}
return end.equals(start.plus(getFreechunkSize(start)));
}
void dump() {
Log.print(fromHeapFreeChunk(this));
Log.print(", ");
Log.print(size.toInt());
}
static void dumpList(HeapFreeChunk head) {
HeapFreeChunk c = head;
while (c != null) {
c.dump();
Log.print(" -> ");
c = c.next;
}
}
static HeapFreeChunk format(Address deadSpace, Size numBytes, Address nextChunk, DynamicHub hub) {
final Pointer cell = deadSpace.asPointer();
if (MaxineVM.isDebug()) {
FatalError.check(hub.isSubClassHub(heapFreeChunkHub().classActor), "Should format with a sub-class of HeapFreeChunk");
if (numBytes.lessThan(heapFreeChunkHeaderSize())) {
Log.print("Dead space @");
Log.print(deadSpace);
Log.print(" size = ");
Log.println(numBytes.toLong());
FatalError.unexpected("Size must be at least a heap free chunk size");
}
Memory.setWords(cell, numBytes.toInt() >> Word.widthValue().log2numberOfBytes, deadSpaceMark());
}
Cell.plantTuple(cell, hub);
Layout.writeMisc(Layout.cellToOrigin(cell), Word.zero());
setFreeChunkSize(cell, numBytes);
setFreeChunkNext(cell, nextChunk);
return toHeapFreeChunk(cell);
}
/**
* Format dead space into a free chunk.
* @param deadSpace pointer to the first word of the dead space
* @param numBytes size of the dead space in bytes
* @return a reference to HeapFreeChunk object just planted at the beginning of the free chunk.
*/
static HeapFreeChunk format(Address deadSpace, Size numBytes, Address nextChunk) {
return format(deadSpace, numBytes, nextChunk, heapFreeChunkHub());
}
@INLINE
public static HeapFreeChunk format(Address deadSpace, Size numBytes) {
return format(deadSpace, numBytes, Address.zero());
}
@INLINE
public static HeapFreeChunk format(Address deadSpace, int numBytes) {
return format(deadSpace, Size.fromInt(numBytes), Address.zero());
}
/**
* Split a chunk. Format the right side of the split as a free chunk, and return
* its address.
* @param chunk the original chunk
*/
static Pointer splitRight(Address chunk, Size leftChunkSize, Address rightNextFreeChunk) {
HeapFreeChunk originalChunk = toHeapFreeChunk(chunk);
Size rightSize = originalChunk.size.minus(leftChunkSize);
HeapFreeChunk rightChunk = format(chunk.plus(leftChunkSize), rightSize, rightNextFreeChunk);
originalChunk.size = leftChunkSize;
return fromHeapFreeChunk(rightChunk).asPointer();
}
@INTRINSIC(UNSAFE_CAST)
private static native HeapFreeChunk asHeapFreeChunk(Object freeChunk);
@INLINE
public static HeapFreeChunk toHeapFreeChunk(Address cell) {
return asHeapFreeChunk(Reference.fromOrigin(Layout.cellToOrigin(cell.asPointer())).toJava());
}
@INLINE
static Address fromHeapFreeChunk(HeapFreeChunk chunk) {
return Layout.originToCell(Reference.fromJava(chunk).toOrigin());
}
public static void formatAsDarkMatter(Address headOfFreeChunkListAddress) {
Address chunkAddress = headOfFreeChunkListAddress;
while (!chunkAddress.isZero()) {
Address start = chunkAddress;
chunkAddress = HeapFreeChunk.getFreeChunkNext(chunkAddress);
DarkMatter.format(start, HeapFreeChunk.getFreechunkSize(start));
}
}
/**
* Find the first chunk in a list of chunks that can accommodate the requested number of bytes.
* @param head a pointer to a HeapFreeChunk
* @param size size in bytes
* @return a chunk of size greater or equal to {code size}, null otherwise.
*/
public static Pointer firstFit(Pointer head, Size size) {
return fromHeapFreeChunk(toHeapFreeChunk(head.getWord().asAddress()).firstFit(size)).asPointer();
}
public static Pointer removeFirst(Pointer head) {
Pointer first = head.getWord().asPointer();
if (!first.isZero()) {
toHeapFreeChunk(first).removeFirstFromList(head);
}
return first;
}
protected void removeFirstFromList(Pointer head) {
head.setWord(fromHeapFreeChunk(next));
}
final HeapFreeChunk firstFit(Size size) {
HeapFreeChunk chunk = this;
while (chunk != null) {
if (chunk.size.greaterEqual(size)) {
return chunk;
}
chunk = chunk.next;
}
return null;
}
/**
* Heap Free Chunk are never allocated.
*/
protected HeapFreeChunk() {
}
/**
* Size of the chunk in bytes (including the size of the instance of HeapFreeChunk prefixing the chunk).
*/
@INSPECTED
Size size;
/**
* A link to a next free chunk in a linked list.
*/
@INSPECTED
HeapFreeChunk next;
}