/*
* $Id$
*
* Copyright (C) 2003-2015 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.vm.memmgr;
import java.io.PrintWriter;
import org.jnode.annotation.Inline;
import org.jnode.annotation.MagicPermission;
import org.jnode.annotation.NoInline;
import org.jnode.vm.Unsafe;
import org.jnode.vm.VmMagic;
import org.jnode.vm.classmgr.VmArray;
import org.jnode.vm.classmgr.VmArrayClass;
import org.jnode.vm.classmgr.VmClassLoader;
import org.jnode.vm.classmgr.VmClassType;
import org.jnode.vm.classmgr.VmNormalClass;
import org.jnode.vm.classmgr.VmType;
import org.jnode.vm.facade.GCStatistics;
import org.jnode.vm.facade.HeapStatistics;
import org.jnode.vm.facade.ObjectFilter;
import org.jnode.vm.facade.VmProcessor;
import org.jnode.vm.facade.VmWriteBarrier;
import org.jnode.vm.objects.VmSystemObject;
import org.vmmagic.unboxed.Address;
import org.vmmagic.unboxed.Extent;
import org.vmmagic.unboxed.ObjectReference;
import org.vmmagic.unboxed.Offset;
/**
* @author Ewout Prangsma (epr@users.sourceforge.net)
*/
@MagicPermission
public abstract class VmHeapManager extends VmSystemObject implements org.jnode.vm.facade.VmHeapManager {
public static final int TRACE_BASIC = 1; // enable basic debugging of GC phases
public static final int TRACE_ALLOC = 2; // enable debugging of GC internal allocation
public static final int TRACE_TRIGGER = 4; // enable debugging of GC triggering / scheduling
public static final int TRACE_OOM = 8; // enable debugging of OOM events
public static final int TRACE_AD_HOC = 16; // enable ad hoc debugging
public static final int TRACE_FLAGS = 31; // all of the above
/**
* Has this manager been initialized yet
*/
private boolean inited = false;
/**
* The current debug flags
*/
protected int heapFlags = TRACE_BASIC | TRACE_OOM | TRACE_ALLOC;
protected OutOfMemoryError OOME;
/**
* The memory access helper
*/
protected final HeapHelper helper;
/**
* Write barrier used
*/
private VmWriteBarrier writeBarrier;
/**
* Initialize this instance
*/
public VmHeapManager(HeapHelper helper) {
this.helper = helper;
}
/**
* Initialize this manager. This method is called before the first call to
* allocObject.
*/
protected abstract void initialize();
/**
* {@inheritDoc}
*/
public abstract void start();
/**
* Create a new instance of a given class
*
* @param cls
* @return The new instance
*/
@Inline
public final Object newInstance(VmType<?> cls) {
return newInstance(cls, ((VmNormalClass<?>) cls).getObjectSize());
}
/**
* {@inheritDoc}
*/
@NoInline
public final Object newInstance(VmType<?> cls, int size) {
testInited();
cls.initialize();
if (cls.isArray()) {
throw new IllegalArgumentException(
"Cannot instantiate an array like this");
}
if (cls.isInterface()) {
throw new IllegalArgumentException(
"Cannot instantiate an interface");
}
final Object obj = allocObject((VmNormalClass<?>) cls, size);
if (obj == null) {
if ((heapFlags & TRACE_OOM) != 0) {
debug("Out of memory");
}
throw OOME;
}
return obj;
}
/**
* {@inheritDoc}
*/
public final Object newArray(VmArrayClass<?> arrayCls, int elements) {
testInited();
if (elements < 0) {
throw new NegativeArraySizeException(
"elements must be greater or equal to 0");
}
if (!arrayCls.isArray()) {
throw new IllegalArgumentException(
"Cannot instantiate a non-array like this ["
+ arrayCls.getName() + ']');
}
final int slotSize = getCurrentProcessor().getArchitecture()
.getReferenceSize();
final int elemSize;
if (arrayCls.isPrimitiveArray()) {
switch (arrayCls.getSecondNameChar()) {
case 'B': // byte
case 'Z': // boolean
elemSize = 1;
break;
case 'C': // char
case 'S': // short
elemSize = 2;
break;
case 'I': // int
case 'F': // float
elemSize = 4;
break;
case 'D': // double
case 'J': // long
elemSize = 8;
break;
default:
throw new IllegalArgumentException(arrayCls.getName());
}
} else {
elemSize = slotSize;
}
arrayCls.incTotalLength(elements);
final Object obj = newArray0(arrayCls, elemSize, elements, slotSize);
if (obj == null) {
if ((heapFlags & TRACE_OOM) != 0) {
debug("Out of memory");
}
throw OOME;
}
return obj;
}
/**
* {@inheritDoc}
*/
public abstract boolean isLowOnMemory();
/**
* {@inheritDoc}
*/
public abstract void gc();
/**
* {@inheritDoc}
*/
public final Object clone(Cloneable object) {
testInited();
final VmClassType<?> objectClass = VmMagic.getObjectType(object);
final Address objectPtr = ObjectReference.fromObject(object)
.toAddress();
final int size;
if (objectClass.isArray()) {
final int slotSize = getCurrentProcessor().getArchitecture()
.getReferenceSize();
final VmArrayClass<?> arrayClass = (VmArrayClass<?>) objectClass;
final int length = objectPtr.loadInt(Offset
.fromIntSignExtend(VmArray.LENGTH_OFFSET * slotSize));
final int elemSize = arrayClass.getComponentType().getTypeSize();
size = (VmArray.DATA_OFFSET * slotSize) + (length * elemSize);
} else {
final VmNormalClass<?> normalClass = (VmNormalClass<?>) objectClass;
size = normalClass.getObjectSize();
}
final Object newObj = allocObject(objectClass, size);
helper.copy(objectPtr, ObjectReference.fromObject(newObj).toAddress(),
Extent.fromIntZeroExtend(size));
return newObj;
}
/**
* {@inheritDoc}
*/
public abstract long getFreeMemory();
/**
* {@inheritDoc}
*/
public abstract long getTotalMemory();
/**
* Allocate a new instance for the given class. Not that this method cannot
* be synchronized, since obtaining a monitor might require creating one,
* which in turn needs this method.
*
* @param vmClass The class to allocate
* @param size The size of the class data, without any header
* @return The allocated object
*/
protected abstract Object allocObject(VmClassType<?> vmClass, int size);
/**
* Create a new instance of an array with a given class, no constructor will
* be called, the object will be filled with zeros.
*
* @param vmClass
* @param elemSize The length in bytes of each element
* @param elements The number of elements
* @param slotSize
* @return The new instance
*/
@Inline
private final Object newArray0(VmClassType vmClass, int elemSize,
int elements, int slotSize) {
final int size = (VmArray.DATA_OFFSET * slotSize)
+ (elemSize * elements);
final Object array = allocObject(vmClass, size);
final Address arrayPtr = ObjectReference.fromObject(array).toAddress();
arrayPtr.store(elements, Offset.fromIntSignExtend(VmArray.LENGTH_OFFSET
* slotSize));
return array;
}
/**
* {@inheritDoc}
*/
public abstract boolean isObject(Address ptr);
@Inline
private final void testInited() {
if (!inited) {
// Unsafe.debug("testInitid.initialize");
initialize();
inited = true;
OOME = new OutOfMemoryError();
// Unsafe.debug("eo-testInitid.initialize");
}
}
/**
* @return Returns the helper.
*/
public final HeapHelper getHelper() {
return this.helper;
}
/**
* {@inheritDoc}
*/
public final VmWriteBarrier getWriteBarrier() {
return writeBarrier;
}
/**
* Sets the write barrier.
* Call this method in the constructor.
*
* @param barrier
*/
protected final void setWriteBarrier(VmWriteBarrier barrier) {
this.writeBarrier = barrier;
}
/**
* {@inheritDoc}
*/
public abstract void dumpStatistics(PrintWriter out);
/**
* {@inheritDoc}
*/
public abstract GCStatistics getStatistics();
/**
* {@inheritDoc}
*/
public abstract HeapStatistics getHeapStatistics(ObjectFilter objectFilter);
/**
* {@inheritDoc}
*/
public abstract Object createProcessorHeapData(VmProcessor cpu);
/**
* {@inheritDoc}
*/
public abstract void notifyClassResolved(VmType<?> vmType);
/**
* Load classes required by this memory manager at build time.
*
* @param loader
*/
public abstract void loadClasses(VmClassLoader loader)
throws ClassNotFoundException;
/**
* {@inheritDoc}
*/
public int getHeapFlags() {
return heapFlags;
}
/**
* {@inheritDoc}
*/
public int setHeapFlags(int reapFlags) {
int res = this.heapFlags;
this.heapFlags = reapFlags;
return res;
}
/**
* Output a debug message controlled by the heap trace flags.
*
* @param text
*/
public void debug(String text) {
if ((heapFlags & TRACE_FLAGS) != 0) {
Unsafe.debug(text);
}
}
/**
* Output a a number as debug message controlled by the heap trace flags.
*
* @param number
*/
protected void debug(int number) {
if ((heapFlags & TRACE_FLAGS) != 0) {
Unsafe.debug(number);
}
}
protected static final org.jnode.vm.scheduler.VmProcessor getCurrentProcessor() {
return org.jnode.vm.scheduler.VmProcessor.current();
}
}