/*
* Copyright (c) 2009, 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;
import static com.sun.max.vm.thread.VmThread.*;
import static com.sun.max.vm.thread.VmThreadLocal.*;
import javax.management.*;
import com.sun.management.*;
import com.sun.max.annotate.*;
import com.sun.max.memory.*;
import com.sun.max.platform.*;
import com.sun.max.unsafe.*;
import com.sun.max.util.*;
import com.sun.max.vm.*;
import com.sun.max.vm.MaxineVM.Phase;
import com.sun.max.vm.actor.holder.*;
import com.sun.max.vm.code.*;
import com.sun.max.vm.heap.debug.*;
import com.sun.max.vm.layout.*;
import com.sun.max.vm.management.*;
import com.sun.max.vm.reference.*;
import com.sun.max.vm.runtime.*;
import com.sun.max.vm.thread.*;
import com.sun.max.vm.thread.VmThreadLocal.Nature;
import com.sun.max.vm.type.*;
/**
* Class to capture common methods for heap scheme implementations.
*/
public abstract class HeapSchemeAdaptor extends AbstractVMScheme implements HeapScheme {
/**
* A VM option for disabling use of TLABs.
*/
public static boolean DisableExplicitGC = false;
static {
VMOptions.addFieldOption("-XX:", "DisableExplicitGC", HeapSchemeAdaptor.class, "Tells whether System.gc() forces a full GC", MaxineVM.Phase.PRISTINE);
}
public class GarbageCollectorMXBeanAdaptor extends MemoryManagerMXBeanAdaptor implements GarbageCollectorMXBean {
public GarbageCollectorMXBeanAdaptor(String name) {
super(name);
}
public GcInfo getLastGcInfo() {
return null;
}
public long getCollectionCount() {
return collectionCount;
}
public long getCollectionTime() {
return accumulatedGCTime;
}
@Override
public ObjectName getObjectName() {
try {
return ObjectName.getInstance("java.lang:type=GarbageCollector,name=" + getName());
} catch (MalformedObjectNameException e) {
throw new IllegalArgumentException(e);
}
}
}
@FOLD
protected static DynamicHub objectHub() {
return ClassRegistry.OBJECT.dynamicHub();
}
@FOLD
protected static DynamicHub byteArrayHub() {
return ClassRegistry.BYTE_ARRAY.dynamicHub();
}
/**
* Size in bytes of an java.lang.Object instance, presumably the minimum object size.
*/
@FOLD
public static Size minObjectSize() {
return objectHub().tupleSize;
}
/**
* Number of words of an java.lang.Object instance, presumably the minimum number of words.
*/
@FOLD
public static int minObjectWords() {
return minObjectSize().unsignedShiftedRight(Word.widthValue().log2numberOfBytes).toInt();
}
@FOLD
public static Size byteArrayHeaderSize() {
return Layout.byteArrayLayout().getArraySize(Kind.BYTE, 0);
}
@FOLD
public static int numByteArrayHeaderWords() {
return byteArrayHeaderSize().unsignedShiftedRight(Word.widthValue().log2numberOfBytes).toInt();
}
/**
* Plants an instance of java.lang.Object at the specified pointer.
*/
private static void plantDeadObject(Pointer cell) {
DebugHeap.writeCellTag(cell);
final Pointer origin = Layout.tupleCellToOrigin(cell);
Memory.clearWords(cell, minObjectWords());
Layout.writeHubReference(origin, Reference.fromJava(objectHub()));
}
/**
* Plants a byte array at the specified cell.
*/
private static void plantDeadByteArray(Pointer cell, Size size) {
DebugHeap.writeCellTag(cell);
final Pointer origin = Layout.arrayCellToOrigin(cell);
Memory.clearWords(cell, numByteArrayHeaderWords());
Layout.writeArrayLength(origin, size.minus(byteArrayHeaderSize()).toInt());
Layout.writeHubReference(origin, Reference.fromJava(byteArrayHub()));
}
@INLINE
public static void fillWithDeadObject(Address start, Address end) {
fillWithDeadObject(start.asPointer(), end.asPointer());
}
/**
* Helper function to fill an area with a (tagged) dead object.
* Used to make a dead area in the heap parseable by GCs.
*
* @param start start of the dead heap area
* @param end end of the dead heap area
*/
public static void fillWithDeadObject(Pointer start, Pointer end) {
Pointer cell = DebugHeap.adjustForDebugTag(start);
Size deadObjectSize = end.minus(cell).asSize();
if (deadObjectSize.greaterThan(minObjectSize())) {
plantDeadByteArray(cell, deadObjectSize);
} else if (deadObjectSize.equals(minObjectSize())) {
plantDeadObject(cell);
} else {
final boolean lockDisabledSafepoints = Log.lock();
Log.printRange(start, end, false);
Log.print(" (");
Log.print(end.minus(start));
Log.print(")");
Log.unlock(lockDisabledSafepoints);
FatalError.unexpected("Not enough space to fit a dead object");
}
}
/**
* Switch to turn off allocation globally.
*/
protected boolean allocationEnabled = true;
/**
* Track pinning support. Default is not supported.
*/
@CONSTANT_WHEN_NOT_ZERO protected int pinningSupportFlags = 0;
/**
* Count of garbage collection performed.
*/
protected int collectionCount;
/**
* Support for {@link GarbageCollectorMXBean#getCollectionTime()}.
*/
protected long accumulatedGCTime;
/**
* Per thread count of request for disabling GC. It allows to fail-fast if a thread pinning an object request garbage collection (which create a deadlock).
*/
public static final VmThreadLocal GC_DISABLING_COUNT =
new VmThreadLocal("GC_DISABLING_COUNT", false, "Count of active GC-disabling requests issued by this thread", Nature.Single);
@HOSTED_ONLY
public HeapSchemeAdaptor() {
}
@Override
public void initialize(MaxineVM.Phase phase) {
super.initialize(phase);
if (phase == MaxineVM.Phase.PRISTINE) {
releaseUnusedReservedVirtualSpace();
}
}
@HOSTED_ONLY
public CodeManager createCodeManager() {
switch (Platform.platform().os) {
case LINUX:
case MAXVE:
case DARWIN:
case SOLARIS: {
// If you change this for any platform above, you may also want to revisit reservedVirtualSpaceSize,
// bootRegionMappingConstraint and the native implementation of mapHeapAndCode.
//
// The default policy implemented by the HeapSchemeAdaptor is to reserve 1 G of virtual space
// (as specified by reservedVirtualSpaceSize) and to memory map the boot region at the start of
// that reserved space (as specified by bootRegionMappingConstraint).
// The FixedAddressCodeManager then initialize itself by allocating the requested size at the end of
// the boot region. This guarantees that all relative displacements in code are 32-bit displacement.
return new NearBootRegionCodeManager();
}
default: {
FatalError.unimplemented();
return null;
}
}
}
public boolean decreaseMemory(Size amount) {
HeapScheme.Inspect.notifyDecreaseMemoryRequested(amount);
return false;
}
public boolean increaseMemory(Size amount) {
HeapScheme.Inspect.notifyIncreaseMemoryRequested(amount);
return false;
}
public void disableAllocationForCurrentThread() {
FatalError.unimplemented();
}
public void enableAllocationForCurrentThread() {
FatalError.unimplemented();
}
public boolean isAllocationDisabledForCurrentThread() {
throw FatalError.unimplemented();
}
public void notifyCurrentThreadDetach() {
// nothing by default
}
@INLINE
public boolean usesTLAB() {
return false;
}
@INLINE
public int objectAlignment() {
return Word.size();
}
@INLINE
public boolean supportsTagging() {
return true;
}
@INLINE
public boolean supportsPadding() {
return true;
}
@INLINE
public void trackLifetime(Pointer cell) {
}
@Override
public void walkHeap(CallbackCellVisitor visitor) {
}
public boolean supportsPinning(PIN_SUPPORT_FLAG flag) {
return flag.isSet(pinningSupportFlags);
}
@INLINE
public boolean needsBarrier(IntBitSet<WriteBarrierSpecification.WriteBarrierSpec> writeBarrierSpec) {
return false;
}
@INLINE
public void preWriteBarrier(Reference ref, Offset offset, Reference value) {
// do nothing
}
@INLINE
public void postWriteBarrier(Reference ref, Offset offset, Reference value) {
// do nothing
}
@INLINE
public void preWriteBarrier(Reference ref, int displacement, int index, Reference value) {
// do nothing
}
@INLINE
public void postWriteBarrier(Reference ref, int displacement, int index, Reference value) {
// do nothing
}
public boolean isPinned(Object object) {
FatalError.check(supportsPinning(PIN_SUPPORT_FLAG.IS_QUERYABLE), "Object pinning support doesn't support querying");
FatalError.unexpected("Must be overriden if supported");
return false;
}
public void disableCustomAllocation() {
final Pointer etla = ETLA.load(currentTLA());
CUSTOM_ALLOCATION_ENABLED.store(etla, Word.zero());
}
public void enableCustomAllocation(Address customAllocator) {
final Pointer etla = ETLA.load(currentTLA());
CUSTOM_ALLOCATION_ENABLED.store(etla, customAllocator);
}
public long maxObjectInspectionAge() {
FatalError.unimplemented();
return 0;
}
public GarbageCollectorMXBean getGarbageCollectorMXBean() {
return new GarbageCollectorMXBeanAdaptor("Invalid") {
@Override
public boolean isValid() {
return false;
}
};
}
public int reservedVirtualSpaceKB() {
// Reserve 1 G of virtual space. This will be used to map the boot heap region and the dynamically allocated code region.
// See comment in createCodeManager
return Size.M.toInt();
}
public BootRegionMappingConstraint bootRegionMappingConstraint() {
// If you modify this, make sure that MS and MSE heap scheme overrides to return AT_START!
return BootRegionMappingConstraint.AT_START;
}
/**
* Release whatever reserved virtual space was left after CodeManager initialization. This is called during {@link Phase#PRISTINE} initialization phase.
* This <b>must</b> be overridden by HeapScheme implementations that either override {@link #createCodeManager()} or {@link #bootRegionMappingConstraint()},
* or make use of the extra space reserved by default.
*/
protected void releaseUnusedReservedVirtualSpace() {
Size reservedVirtualSpaceSize = Size.K.times(VMConfiguration.vmConfig().heapScheme().reservedVirtualSpaceKB());
if (reservedVirtualSpaceSize.isZero()) {
return;
}
FatalError.check(bootRegionMappingConstraint() == BootRegionMappingConstraint.AT_START,
"Overridding HeapSchemeAdaptor.bootRegionMappingConstraint() mandates to override HeapSchemeAdaptor.releaseUnusedReservedVirtualSpace");
FatalError.check(NearBootRegionCodeManager.class == Code.getCodeManager().getClass(),
"Overridding HeapSchemeAdaptor.createCodeManager mandates to override HeapSchemeAdaptor.releaseUnusedReservedVirtualSpace");
Address startOfReservedVirtualSpaceSize = Heap.bootHeapRegion.start();
Address endOfReservedVirtualSpaceSize = startOfReservedVirtualSpaceSize.plus(reservedVirtualSpaceSize);
checkRuntimeCodeRegion(startOfReservedVirtualSpaceSize, endOfReservedVirtualSpaceSize, Code.getCodeManager().getRuntimeBaselineCodeRegion());
checkRuntimeCodeRegion(startOfReservedVirtualSpaceSize, endOfReservedVirtualSpaceSize, Code.getCodeManager().getRuntimeOptCodeRegion());
Address startOfUnusedVirtualSpace = Code.getCodeManager().getRuntimeOptCodeRegion().end().alignUp(Platform.platform().pageSize);
Size unusedVirtualSpaceSize = endOfReservedVirtualSpaceSize.minus(startOfUnusedVirtualSpace).asSize();
if (!unusedVirtualSpaceSize.isZero()) {
VirtualMemory.deallocate(startOfUnusedVirtualSpace, unusedVirtualSpaceSize, VirtualMemory.Type.DATA);
}
}
private void checkRuntimeCodeRegion(Address startOfReservedVirtualSpaceSize, Address endOfReservedVirtualSpaceSize, MemoryRegion codeRegion) {
FatalError.check(startOfReservedVirtualSpaceSize.lessThan(codeRegion.start()) && codeRegion.end().lessEqual(endOfReservedVirtualSpaceSize),
"Runtime code region should be in virtual space reserved by boot loader");
}
public boolean isGcThread(Thread thread) {
// Adaptor assume single-threaded GC operating on the VmOperationThread.
// Override if not true.
return thread instanceof VmOperationThread;
}
}