/*
* Copyright (c) 2007, 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 java.lang.management.*;
import com.sun.max.annotate.*;
import com.sun.max.memory.*;
import com.sun.max.profile.*;
import com.sun.max.unsafe.*;
import com.sun.max.util.*;
import com.sun.max.vm.*;
import com.sun.max.vm.actor.holder.*;
import com.sun.max.vm.code.*;
import com.sun.max.vm.log.*;
import com.sun.max.vm.object.*;
import com.sun.max.vm.reference.*;
import com.sun.max.vm.runtime.*;
import com.sun.max.vm.tele.*;
import com.sun.max.vm.thread.*;
import com.sun.max.vm.type.*;
public interface HeapScheme extends VMScheme {
/**
* Thread-local GC request to store parameters and results between a thread issuing a GC request and the VM operation.
* Every thread is given one and uses it to communicate details regarding its requests for garbage collection.
* This class can be extended to allow additional heap scheme specific information to be passed to a GC operation.
*/
public static abstract class GCRequest {
public final VmThread requester;
/**
* Amount of bytes requested if this request was issued by a failed allocation operation.
*/
public Size requestedBytes;
/**
* Invocation count of the last satisfied request.
* See {@link GCOperation#invocationCount()}.
*/
public int lastInvocationCount;
/**
* Indicate whether this request is the result of a {@linkplain java.lang.Runtime#gc()} call.
*/
public boolean explicit;
public void clear() {
explicit = false;
requestedBytes = Size.zero();
}
protected GCRequest(VmThread requester) {
this.requester = requester;
}
public void printBeforeGC() {
final boolean lockDisabledSafepoints = Log.lock();
Log.print("--GC requested by thread ");
Log.printThread(requester, false);
Log.print(" for ");
if (explicit) {
Log.print("Explicit GC");
} else {
Log.print(requestedBytes.toLong());
Log.println(" bytes --");
}
Log.unlock(lockDisabledSafepoints);
}
public void printAfterGC(boolean satisfied) {
final boolean lockDisabledSafepoints = Log.lock();
Log.print("--GC requested by thread ");
Log.printThread(requester, false);
if (explicit) {
Log.println("completed");
} else {
if (satisfied) {
Log.println(" freed enough--");
} else {
Log.println(" did not free enough--");
}
}
Log.unlock(lockDisabledSafepoints);
}
/**
* Convenience for clearing the {@linkplain GCRequest} of the current thread before returning it.
* @return the {@linkplain GCRequest} for the current thread.
*/
public static GCRequest clearedGCRequest() {
final GCRequest gcRequest = VmThread.current().gcRequest;
gcRequest.clear();
return gcRequest;
}
/**
* Convenience for setting {@linkplain GCRequest#requestedBytes} for the current thread.
* The GC request is cleared beforehand.
*
* @param requestedBytes number of bytes.
*/
public static void setGCRequest(Size requestedBytes) {
final GCRequest gcRequest = clearedGCRequest();
gcRequest.requestedBytes = requestedBytes;
}
}
/**
* Indicates the boot image loader where the boot region should be mapped in the virtual address space: anywhere (i.e., anywhere inside),
* at the beginning of the reserved virtual space, or at the end.
* @see HeapScheme#bootRegionMappingConstraint()
* @see HeapScheme#reservedVirtualSpaceKB()
*/
public enum BootRegionMappingConstraint {
ANYWHERE,
AT_START,
AT_END
}
/**
* The clock that specifies the timing resolution for GC related timing.
*/
Clock GC_TIMING_CLOCK = Clock.SYSTEM_MILLISECONDS;
/**
* Determines whether a given thread belongs to the garbage collector. This method is only called outside of
* the garbage collector and so it is safe for the implementation to use the complete Java language semantics
* including {@code instanceof}.
*
* This method is only called from a {@link VmThread} constructor. Subsequent tests for whether a given {@code VmThread}
* instance is a GC thread should use the {@link VmThread#isVmOperationThread()} method directly.
*
* @return whether a thread belongs to the GC (or otherwise it belongs to the mutator)
*/
boolean isGcThread(Thread thread);
/**
* Return the amount of virtual space (in KB) that must be reserved by the boot image loader at boot-image load time.
* The boot loader may map the boot region in this reserved virtual space at a location specified by {@link #bootRegionMappingConstraint()}.
*
* This helps with heap schemes that require the heap to be contiguous, or at a specific location (above or below) with respect to the boot region.
* By default, this method assumes the heap scheme doesn't require any space contiguous to the boot image and returns 0, which indicates to the boot image
* loader that it only needs to reserve what's needed for the boot image.
* This mechanism may also be used to guarantee that code region aren't farther than a given distance from the boot code region (see {@link NearBootRegionCodeManager}).
*
* @return a number of KB
*/
int reservedVirtualSpaceKB();
/**
* Indicate to the boot image loader where the boot image should be located in virtual space with respect to the contiguous range of virtual space
* that the boot image loader may have reserved for the heap scheme based on the result of {@link #reservedVirtualSpaceKB()}.
*
* @return a boot image constraint.
*/
BootRegionMappingConstraint bootRegionMappingConstraint();
/**
* Allocate a new array object and fill in its header and initial data.
*/
Object createArray(DynamicHub hub, int length);
/**
* Allocate a new tuple and fill in its header and initial data. Obtain the cell size from the given tuple class
* actor.
*/
Object createTuple(Hub hub);
/**
* Creates a hybrid object that is both a tuple and an array,
* but leaving out the array part beyond the tuple for now.
*/
Object createHybrid(DynamicHub hub);
/**
* Expands the hybrid object to its full array length.
* The implementation may modify the original object and return it
* or it can create a new object that contains the same tuple values and return that.
*/
Hybrid expandHybrid(Hybrid hybrid, int length);
/**
* Creates a shallow clone of an object.
* The identity hash value may differ.
* The new object is not locked.
*/
Object clone(Object object);
/**
* Returns whether an address is anywhere in the range of addresses managed by the heap scheme.
*/
boolean contains(Address address);
/**
* Create a thread-local GC Request for a vmThread.
*
* @param vmThread the thread this thread-local GC Request is for.
* @return a heap scheme specific sub class of {@linkplain GCRequest}
*/
GCRequest createThreadLocalGCRequest(VmThread vmThread);
/**
* Request a garbage collection.
* The details of the request are described in the requesting thread's GC request (@see {@link GCRequest}).
*
* @return {@code true} if the request was satisfied, {@code false} otherwise
*/
boolean collectGarbage();
/**
* Gets the amount of free memory on the heap. This method does not trigger a GC.
*
* @return an approximation to the total amount of memory currently
* available for future allocated objects, measured in bytes.
*/
Size reportFreeSpace();
/**
* Gets the amount of memory used by objects allocated on the heap. This method does not trigger a GC.
*
* @return an approximation to the total amount of memory currently
* used by allocated objects, measured in bytes.
*/
Size reportUsedSpace();
/**
* Returns the maximum <em>object-inspection age</em>, which is the number
* of real-time milliseconds that have elapsed since the
* least-recently-inspected heap object was last inspected by the garbage
* collector.
*
* <p> For simple stop-the-world collectors this value is just the time
* since the most recent collection. For generational collectors it is the
* time since the oldest generation was most recently collected. Other
* collectors are free to return a pessimistic estimate of the elapsed
* time, or simply the time since the last full collection was performed.
*
* <p> Note that in the presence of reference objects, a given object that
* is no longer strongly reachable may have to be inspected multiple times
* before it can be reclaimed.
* @return a time in milliseconds
*/
long maxObjectInspectionAge();
/**
* A request for the heap scheme to attempt to reduce its memory usage.
* This interface is optional and a default implementation can just return false.
* @param amount suggested amount to reduce
* @return true if can/will reduce, false otherwise
*/
boolean decreaseMemory(Size amount);
/**
* A hint that the heap scheme can increase its memory usage.
* This interface is optional and a default implementation can just return false.
* @param amount suggested amount to increase
* @return true if can/will increase memory usage, false otherwise
*/
boolean increaseMemory(Size amount);
/**
* Disables heap allocation on the current thread. This state is recursive. That is,
* allocation is only re-enabled once {@link #enableAllocationForCurrentThread()} is
* called the same number of times as this method has been called.
*
* It is a {@linkplain FatalError fatal error} if calls to this method and {@link #enableAllocationForCurrentThread()}
* are unbalanced.
*/
void disableAllocationForCurrentThread();
/**
* Re-enables heap allocation on the current thread. This state is recursive. That is,
* allocation is only re-enabled once this method is
* called the same number of times as {@link #disableAllocationForCurrentThread()} has been called.
*
* It is a {@linkplain FatalError fatal error} if calls to this method and {@link #disableAllocationForCurrentThread()}
* are unbalanced.
*/
void enableAllocationForCurrentThread();
/**
* Determines if heap allocation is disabled on the current thread.
*/
boolean isAllocationDisabledForCurrentThread();
boolean needsBarrier(IntBitSet<WriteBarrierSpecification.WriteBarrierSpec> writeBarrierSpec);
void preWriteBarrier(Reference ref, Offset offset, Reference value);
void postWriteBarrier(Reference ref, Offset offset, Reference value);
void preWriteBarrier(Reference ref, int displacement, int index, Reference value);
void postWriteBarrier(Reference ref, int displacement, int index, Reference value);
enum PIN_SUPPORT_FLAG {
/**
* Just to indicate that the pin support flag has been initialized (makes the pinningSupportFlags treated as constant when not zero).
*/
IS_INITIALIZED,
/**
* Is object pinning supported at all.
*/
IS_SUPPORTED,
/**
* Is it possible to call pin (respectively unpin) on the same object multiple times ?
* If not possible, pinning must be at least queryable
*/
CAN_NEST,
/**
* Is querying on individual object pinning status supported ?
*/
IS_QUERYABLE;
private final int mask = 1 << ordinal();
public final boolean isSet(int flags) {
return (flags & mask) != 0;
}
private int or(int flags) {
return flags | mask;
}
public static final int makePinSupportFlags(boolean supported, boolean queryable, boolean canNest) {
int flags = IS_INITIALIZED.or(0);
if (supported) {
flags = IS_SUPPORTED.or(flags);
if (canNest) {
flags = CAN_NEST.or(flags);
}
if (queryable) {
flags = IS_QUERYABLE.or(flags);
} else {
FatalError.check(canNest, "Must be able to nest pin request if not queryable");
}
}
return flags;
}
}
boolean supportsPinning(PIN_SUPPORT_FLAG flag);
/**
* Prevent the GC from moving the given object.
*
* Allocating very small amounts in the same thread before unpinning is strongly discouraged but not strictly
* forbidden.
*
* Pinning and then allocating may cause somewhat premature OutOfMemoryException. However, the implementation is
* supposed to not let pinning succeed in the first place if there is any plausible danger of that happening.
*
* Pinning and then allocating large amounts is prone to cause premature OutOfMemoryException.
*
* Example:
*
* ATTENTION: The period of time an object can remained pinned must be "very short". Pinning may block other threads
* that wait for GC to happen. Indefinite pinning will create deadlock!
*
* Calling this method on an already pinned object has undefined consequences.
*
* Note to GC implementors: you really don't need to implement pinning. It's an entirely optional/experimental
* feature. However, if present, there are parts of the JVM that will automatically take advantage of it.
*
* If pinning is not supported, make pin() and isPinned() always return false.
* Then all pinning client code will automatically be eliminated.
*
* @return whether pinning succeeded - callers are supposed to have an alternative plan when it fails
*/
boolean pin(Object object);
/**
* Allow the given object to be moved by the GC. Always quickly balance each call to the above with a call to this
* method.
*
* Calling this method on an already unpinned object has undefined consequences.
*/
void unpin(Object object);
/**
* Return true if the given object is already pinned. This method must be called only when IS_QUERYABLE is false.
* @param object
*/
boolean isPinned(Object object);
/**
* Is TLAB used in this heap scheme or not.
* @return true if TLAB is used
*/
boolean usesTLAB();
/**
* Object alignment required by the heap manager (in number of bytes).
* @return number of bytes.
*/
int objectAlignment();
/**
* Turns custom memory allocation on for the current thread. All memory allocations performed by the current thread
* happen in the allocator identified by the customAllocator value.
*
* Should be used like:
* try {
* Heap.enableCustomAllocation(customAllocatorID);
* ...
* <allocations are now performed by the custom allocator>
* ...
* } finally {
* Heap.disableCustomAllocationAllocation();
* }
*
*/
void enableCustomAllocation(Address customAllocator);
/**
* Turns custom memory allocation off for the current thread. All memory allocations performed by the current thread
* happen in the garbage collected heap.
*/
void disableCustomAllocation();
/**
* Announces that the current thread is detaching from the VM so that
* thread-local resources (e.g. TLABs) can be released.
*/
void notifyCurrentThreadDetach();
/**
* Creates the singleton code manager for the VM.
* @return a new code manager
*/
@HOSTED_ONLY
CodeManager createCodeManager();
/**
* Returns the garbage collection management bean for this heap scheme.
* @return the {@link GarbageCollectorMXBean} instance
*/
GarbageCollectorMXBean getGarbageCollectorMXBean();
/**
* Indicates whether this heap scheme supports tagging of heap object for debugging purposes.
* @return true if tagging of heap object is supported
*/
boolean supportsTagging();
/**
* Indicates whether this heap scheme supports padding of unallocated space for debugging purposes.
* @return true if padding is supported
*/
boolean supportsPadding();
/**
* Encapsulates the structure of the heap from a tool (e.g. JVMTI) that want to visit every object
* in the heap.
*
* @param visitor
*/
void walkHeap(CallbackCellVisitor visitor);
/*
* Logging support.
*/
public static abstract class PhaseLogger extends VMLogger {
/**
* Create instance. For auto-generation we keep the {@link VMLogger} constructor form.
*/
protected PhaseLogger(String name, int numOps, String description, int[] refMaps) {
super("GCPhases", numOps, "garbage collection phases.", refMaps);
}
/**
* All heap schemes scan the {@link VmThreadLocal} references and thread stack roots.
* @param vmThread
*/
public abstract void logScanningThreadRoots(VmThread vmThread);
}
/**
* Get the concrete implementation of {@link PhaseLogger}.
*/
PhaseLogger phaseLogger();
/**
* A logger for GC timings - implementation provided by the heap scheme.
*/
public static abstract class TimeLogger extends VMLogger {
/**
* Create instance. For auto-generation we keep the {@link VMLogger} constructor form.
*/
protected TimeLogger(String name, int numOps, String description, int[] refMaps) {
super("GCTime", numOps, "time of garbage collection phases.", refMaps);
}
/**
* Every scheme has this phase.
*
* @param stackReferenceMapPreparationTime
*/
public abstract void logStackReferenceMapPreparationTime(long stackReferenceMapPreparationTime);
}
/**
* Get the concrete implementation of {@link TimeLogger}.
*/
TimeLogger timeLogger();
/**
* A collection of methods that support certain inspection services.
* The public methods are to be called by all scheme implementations when
* the specified events occur.
*/
public static final class Inspect {
/**
* Sets up machinery for inspection of heap activity.
* <p>
* No-op when VM is not being inspected.
* @param useImmortalMemory should allocations should be made in immortal memory.
*/
public static void init(boolean useImmortalMemory) {
InspectableHeapInfo.init(useImmortalMemory);
}
/**
* Announces the collection of memory regions currently being
* used for the heap. This should be called whenever the
* collection changes.
* <p>
* No-op when VM is not being inspected.
*/
public static void notifyHeapRegions(MemoryRegion... memoryRegions) {
InspectableHeapInfo.setMemoryRegions(memoryRegions);
}
/**
* Announces that the heap's GC has just changed to a new phase. It does almost nothing,
* but it must be called by GC implementations for certain Inspector services to work.
* <p>
* It is assumed that the heap pass through the three phases in the following order:
* <ol>
* <li>{@link HeapPhase#MUTATING}: this is the initial phase, and a transition
* into this phase is taken to be the conclusion of a GC.</li>
* <li>{@link HeapPhase#ANALYZING}: a transition into this phase is taken to be
* the beginning of a GC.</li>
* <li>{@link HeapPhase#RECLAIMING}: an internal transition within a GC, the point
* in the GC where analysis has been concluded, without loss of historical information.</li>
* </ol>
*/
public static void notifyHeapPhaseChange(HeapPhase phase) {
InspectableHeapInfo.notifyPhaseChange(phase);
switch (phase) {
case ANALYZING:
inspectableGCStarting();
break;
case RECLAIMING:
inspectableGCReclaiming();
break;
case MUTATING:
inspectableGCCompleted();
break;
}
}
/**
* Announces that a request has been made to increase the size of the heap.
* It does nothing, but it must be called for certain Inspector services to work.
* <p>
* This method should be called by implementations first thing when the request
* is received, before action is taken.
*
* @param size
* @see HeapScheme#increaseMemory(Size)
*/
public static void notifyIncreaseMemoryRequested(Size size) {
InspectableHeapInfo.notifyIncreaseMemoryRequested(size);
inspectableIncreaseMemoryRequested(size);
}
/**
* Announces that a request has been made to decrease the size of the heap.
* It does nothing, but it must be called for certain Inspector services to work.
* <p>
* This method should be called by implementations first thing when the request
* is received, before action is taken.
*
* @param size
* @see HeapScheme#decreaseMemory(Size)
*/
public static void notifyDecreaseMemoryRequested(Size size) {
InspectableHeapInfo.notifyDecreaseMemoryRequested(size);
inspectableDecreaseMemoryRequested(size);
}
private Inspect() {
}
/**
* An empty method whose purpose is to be interrupted by the Inspector
* at the beginning of a GC, i.e. when the phase has just changed to {@link HeapPhase#ANALYZING}.
* <p>
* This particular method is intended for use by users of the Inspector, and
* is distinct from a method used by the Inspector for internal use.
* <p>
* <strong>Important:</strong> The Inspector assumes that this method is loaded
* and compiled in the boot image and that it will never be dynamically recompiled.
*/
@INSPECTED
@NEVER_INLINE
private static void inspectableGCStarting() {
}
// Ensure that the above method is compiled into the boot image so that it can be inspected conveniently
private static CriticalMethod inspectableGCStartingCriticalMethod =
new CriticalMethod(HeapScheme.Inspect.class, "inspectableGCStarting", SignatureDescriptor.create(void.class));
/**
* An empty method whose purpose is to be interrupted by the Inspector just before the GC starts
* cleaning up, i.e. when the phase has just changed to {@link HeapPhase#RECLAIMING}.
* <p>
* This particular method is intended for use by users of the Inspector, and
* is distinct from a method used by the Inspector for internal use.
* <p>
* <strong>Important:</strong> The Inspector assumes that this method is loaded
* and compiled in the boot image and that it will never be dynamically recompiled.
*/
@INSPECTED
@NEVER_INLINE
private static void inspectableGCReclaiming() {
}
// Ensure that the above method is compiled into the boot image so that it can be inspected conveniently
private static CriticalMethod inspectableGCReclaimingCriticalMethod =
new CriticalMethod(HeapScheme.Inspect.class, "inspectableGCReclaiming", SignatureDescriptor.create(void.class));
/**
* An empty method whose purpose is to be interrupted by the Inspector
* at the conclusions of a GC, i.e. when the phase has just changed back to {@link HeapPhase#MUTATING}.
* <p>
* This particular method is intended for use by users of the Inspector, and
* is distinct from a method used by the Inspector for internal use.
* <p>
* <strong>Important:</strong> The Inspector assumes that this method is loaded
* and compiled in the boot image and that it will never be dynamically recompiled.
*/
@INSPECTED
@NEVER_INLINE
private static void inspectableGCCompleted() {
}
// Ensure that the above method is compiled into the boot image so that it can be inspected conveniently
private static CriticalMethod inspectableGCCompletedCriticalMethod =
new CriticalMethod(HeapScheme.Inspect.class, "inspectableGCCompleted", SignatureDescriptor.create(void.class));
/**
* An empty method whose purpose is to be interrupted by the Inspector
* at the conclusions of an object relocation..
* <p>
* This particular method is intended for use by users of the Inspector, and
* is distinct from a method used by the Inspector for internal use.
* <p>
* <strong>Important:</strong> The Inspector assumes that this method is loaded
* and compiled in the boot image and that it will never be dynamically recompiled.
*/
@INSPECTED
@NEVER_INLINE
private static void inspectableObjectRelocated(Address oldCellLocation, Address newCellLocation) {
}
// Ensure that the above method is compiled into the boot image so that it can be inspected conveniently
private static CriticalMethod inspectableObjectRelocatedCriticalMethod =
new CriticalMethod(HeapScheme.Inspect.class, "inspectableObjectRelocated", SignatureDescriptor.create(void.class, Address.class, Address.class));
/**
* An empty method whose purpose is to be interrupted by the Inspector
* when a change in heap size is requested.
* <p>
* This particular method is intended for use by users of the Inspector.
* <p>
* <strong>Important:</strong> The Inspector assumes that this method is loaded
* and compiled in the boot image and that it will never be dynamically recompiled.
*
* @see HeapScheme#increaseMemory(Size)
*/
@INSPECTED
@NEVER_INLINE
private static void inspectableIncreaseMemoryRequested(Size size) {
}
// Ensure that the above method is compiled into the boot image so that it can be inspected conveniently
private static CriticalMethod inspectableIncreaseMemoryRequestedCriticalMethod =
new CriticalMethod(HeapScheme.Inspect.class, "inspectableIncreaseMemoryRequested", SignatureDescriptor.create(void.class, Size.class));
/**
* An empty method whose purpose is to be interrupted by the Inspector
* when a change in heap size is requested.
* <p>
* This particular method is intended for use by users of the Inspector.
* <p>
* <strong>Important:</strong> The Inspector assumes that this method is loaded
* and compiled in the boot image and that it will never be dynamically recompiled.
*
* @see HeapScheme#decreaseMemory(Size)
*/
@INSPECTED
@NEVER_INLINE
private static void inspectableDecreaseMemoryRequested(Size size) {
}
// Ensure that the above method is compiled into the boot image so that it can be inspected conveniently
private static CriticalMethod inspectableDecreaseMemoryRequestedCriticalMethod =
new CriticalMethod(HeapScheme.Inspect.class, "inspectableDecreaseMemoryRequested", SignatureDescriptor.create(void.class, Size.class));
}
}