/*
* Copyright (c) 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.tele.heap;
import static com.sun.max.vm.heap.HeapPhase.*;
import java.io.*;
import java.lang.management.*;
import java.text.*;
import java.util.*;
import com.sun.max.lang.*;
import com.sun.max.memory.*;
import com.sun.max.program.*;
import com.sun.max.tele.*;
import com.sun.max.tele.MaxMarkBitmap.MarkColor;
import com.sun.max.tele.TeleVM.InitializationListener;
import com.sun.max.tele.debug.*;
import com.sun.max.tele.heap.region.*;
import com.sun.max.tele.object.*;
import com.sun.max.tele.reference.*;
import com.sun.max.tele.reference.ms.*;
import com.sun.max.tele.util.*;
import com.sun.max.unsafe.*;
import com.sun.max.vm.heap.*;
import com.sun.max.vm.heap.gcx.*;
import com.sun.max.vm.heap.gcx.ms.*;
import com.sun.max.vm.runtime.*;
/**
* <p>
* Inspection support specialized for the basic {@linkplain MSHeapScheme mark-sweep implementation} of {@link HeapScheme}
* in the VM.</p>
* <p>
* This support will not function correctly unless the VM is built in DEBUG mode, in which case the GC will
* {@linkplain Memory#ZAPPED_MARKER zap} all memory in free space with one exception.
* The exception is the headers of <em>quasi-objects</em> used by the GC to represent free space explicitly.
* Examples include instances of {@link HeapFreeChunk}, {@link DarkMatter}, and {@link DarkMatter.SmallestDarkMatter}.
* This formatting is needed, among other reasons, to allow the GC to sweep all of memory.</p>
* <p>
* The term <em>quasi-object</em> refers to an area of memory formatted as if it were an ordinary Maxine
* VM object, but which is
* <ul>
* <li>only known to the GC implementation;</li>
* <li>is never given ordinary object behavior; and </li>
* <li>is never reachable from any object roots.</li>
* </ul></p>
* <p>
* This support further depends on the use of <em>precise scanning</em> during the GC {@linkplain HeapPhase#RECLAIMING reclaiming} phase,
* in which <em>dark matter</em> objects (those determined to be unreachable but not worth reclaiming) are specially zapped and
* formatted for convenient recognition.</p>
* <p>
* This implementation maintains two <em>maps</em> from VM memory locations to {@link RemoteReference}s: an <em>Object Map</em>
* and a <em>Free Space Map</em>. A {@link RemoteReference}s represents the <em>identity</em> of an object (whether legitimate or quasi-)
* in the VM. Note that this perspective differs somewhat from that of the GC implementation.</p>
* <p>
* General map invariants:
* <ul>
* <li>The <em>Object Map</em> only holds references that are {@linkplain ObjectStatus#LIVE LIVE} or
* {@linkplain ObjectStatus#UNREACHABLE UNREACHABLE}.</li>
* <li>The <em>Free Space Map</em> only holds references that are {@linkplain ObjectStatus#FREE FREE} or
* {@linkplain ObjectStatus#DARK DARK}.</li>
* <li>The maps never contain {@linkplain ObjectStatus#DEAD DEAD} references.</li>
* <li>A single memory location never appears simultaneously in both maps, which is to say there can
* only be one reference (and one kind of object) at a location.</li>
* <li>A reference never moves from one map to another, consistent with the reference state transitions expressed
* by the specific implementation of references used for these maps ({@link MSRemoteReference}). In particular,
* the only identity-preserving state transition that occurs within the maps is from {@linkplain ObjectStatus#LIVE LIVE}
* to {@linkplain ObjectStatus#UNREACHABLE UNREACHABLE}. Every other change in status at a memory location is modeled
* here by the replacement of one object by another with separate identity (e.g. a new chunk of free space <em>replaces</em> an object,
* the old object becomes {@linkplain ObjectStatus#DEAD DEAD} and its reference is removed from the map).</li>
* </ul></p>
* <p>
* The following description enumerates more specifically what remote inspection can observe during each {@link HeapPhase}.
* The <em>canonical</em> relationship between references and VM objects allows the distinction between
* the two to be blurred for brevity in this description.</p>
* <p>
* <b>{@link HeapPhase#MUTATING} Summary</b>
* <p>
* During this phase new objects are allocated by splitting (if needed) {@linkplain ObjectStatus#FREE FREE} quasi-object instances.
* Newly allocated objects appear, and {@linkplain ObjectStatus#FREE FREE} quasi-objects both appear and disappear (are overwritten).</p>
* <ul>
* <li><b>Object Map:</b>
* <ol>
* <li>The Object Map contains only {@linkplain ObjectStatus#LIVE LIVE} object references.</li>
* <li>Previously unseen {@linkplain ObjectStatus#LIVE LIVE} objects may be discovered and added to the Object Map.</li>
* <li>No other changes to the Object Map take place during the {@linkplain HeapPhase#MUTATING mutating} phase.</li>
* </ol></li>
* <li><b>Free Space Map:</b>
* <ol>
* <li>The Free Space map contains only {@linkplain ObjectStatus#FREE FREE} or {@linkplain ObjectStatus#DARK DARK}
* quasi-object references.</li>
* <li>Previously unseen {@linkplain ObjectStatus#FREE FREE} quasi-objects, possibly newly created, may be discovered
* and added to the Free Space Map.</li>
* <li>Previously unseen {@linkplain ObjectStatus#DARK DARK} quasi-objects may be discovered and added to the
* Free Space Map, but they do not change during this phase.</li>
* <li>A {@linkplain ObjectStatus#FREE FREE} quasi-object reference in the map may be discovered, by observing that its hub pointer
* has changed, to have been <em>overwritten</em>: i.e. replaced by an object allocation. This reference becomes
* {@linkplain ObjectStatus#DEAD DEAD} and is removed from the Free Space Map.</li>
* <li>{@linkplain ObjectStatus#FREE FREE} quasi-objects in the map do not change in size, on the assumption that allocation
* out of a {@linkplain ObjectStatus#FREE FREE} quasi-object always happens at the beginning,
* so the {@linkplain ObjectStatus#FREE FREE} quasi-object becomes {@linkplain ObjectStatus#DEAD DEAD} and
* another is created with the remaining space (if there is enough).</li>
* <li>No other changes to the Free Space Map take place during the {@linkplain HeapPhase#MUTATING mutating} phase.</li>
* </ol></li></ul>
* <p>
*
* <b>{@link HeapPhase#ANALYZING} Summary</b>
* <p>
* During this phase the only observable change is the transition of some heap object locations from <em>unmarked</em> to
* <em>marked</em>. A <em>mark</em> appears when the GC has determined the object to be <em>reachable</em>,
* whereas an <em>unmarked</em> location signified an object whose reachability remains unknown.</p>
* <ul>
* <li><b>Object Map:</b>
* <ol>
* <li>The Object Map contains only {@linkplain ObjectStatus#LIVE LIVE} references, whether or not
* they are <em>marked</em>.</li>
* <li>New {@linkplain ObjectStatus#LIVE LIVE} objects may be discovered and added to the Object Map.</li>
* <li>Unmarked objects in the map may become marked.</li>
* <li>No other changes to the Object Map take place during the {@linkplain HeapPhase#ANALYZING analyzing} phase.</li>
* </ol></li>
* <li><b>Free Space Map:</b>
* <ol>
* <li>The Free Space map contains only {@linkplain ObjectStatus#FREE FREE} or
* {@linkplain ObjectStatus#DARK DARK} quasi-object references.</li>
* <li>Previously unseen {@linkplain ObjectStatus#FREE FREE} quasi-objects may be discovered
* and added to the Free Space Map, but they do not change during this phase.</li>
* <li>Previously unseen {@linkplain ObjectStatus#DARK DARK} quasi-objects may be discovered and added to the
* Free Space Map, but they do not change during this phase.</li>
* <li>No other changes to the Free Space Map take place during the {@linkplain HeapPhase#ANALYZING analyzing} phase.</li>
* </ol></li></ul>
* <p>
* <b>{@link HeapPhase#RECLAIMING} Summary</b>
* <p>
* During this phase the memory holding unreachable (<em>unmarked</em>) objects is reclaimed as <em>free space</em>, either
* as {@linkplain ObjectStatus#FREE FREE} quasi-objects or (if judged too small to be worth managing)
* {@linkplain ObjectStatus#DARK DARK} quasi-objects (<em>dark matter</em>).
* Unreachable objects disappear. {@linkplain ObjectStatus#FREE FREE} quasi-objects both appear and disappear as they are merged into
* larger instances. Previously created {@linkplain ObjectStatus#DARK DARK} quasi-objects may also be reclaimed and merged
* with other {@linkplain ObjectStatus#FREE FREE} quasi-objects.
* </p>
* <ul>
* <li><b>Object Map:</b>
* <ol>
* <li>The Object Map contains only object references that are {@linkplain ObjectStatus#LIVE LIVE} (if marked as reachable during
* the preceding {@linkplain HeapPhase#ANALYZING analyzing} phase) or {@linkplain ObjectStatus#UNREACHABLE UNREACHABLE} (if unmarked).</li>
* <li>Marking does not change during this phase.</li>
* <li>A previously unseen marked object may be discovered and added to the Object Map as {@linkplain ObjectStatus#LIVE LIVE}.</li>
* <li>A previously unseen unmarked object may be discovered and added to the Object Map as {@linkplain ObjectStatus#UNREACHABLE UNREACHABLE}.</li>
* <li>An {@linkplain ObjectStatus#UNREACHABLE UNREACHABLE} object in the Object Map might be discovered to have been <em>overwritten</em>
* through merging with another {@linkplain ObjectStatus#FREE FREE} quasi-object (by observing that the header has been zapped).
* This reference becomes {@linkplain ObjectStatus#DEAD DEAD} and is removed from the Object Map.</li>
* <li>An {@linkplain ObjectStatus#UNREACHABLE UNREACHABLE} object in the Object Map might be discovered to have been <em>overwritten</em>
* through replacement by a {@linkplain ObjectStatus#FREE FREE} quasi-object (by observing that the header has been replaced).
* This reference becomes {@linkplain ObjectStatus#DEAD DEAD} and is removed from the Object Map.
* <ul>
* <li><strong>Note:</strong> in this event we might proactively create a new quasi-reference for the newly discovered
* {@linkplain ObjectStatus#FREE FREE} quasi-object and add it to the Free Space Map.</li>
* <li><strong>Note:</strong> in this event we might convert an existing <em>view</em> on the
* formerly {@linkplain ObjectStatus#UNREACHABLE UNREACHABLE} object to a view on the newly discovered {@linkplain ObjectStatus#FREE FREE} quasi-object
* that replaced it (or should it be converted to a dead object view?).</li>
* </ul></li>
* <li>An {@linkplain ObjectStatus#UNREACHABLE UNREACHABLE} object in the Object Map might be discovered to have been <em>overwritten</em>
* through replacement by an instance of {@linkplain ObjectStatus#DARK DARK} matter (by observing that the header has been replaced).
* This reference becomes {@linkplain ObjectStatus#DEAD DEAD} and is removed from the Object Map.
* <ul>
* <li><strong>Note:</strong> in this event we might proactively create a new quasi-reference for the newly discovered
* {@linkplain ObjectStatus#DARK DARK} matter and add it to the Free Space Map.</li>
* <li><strong>Note:</strong> in this event we might convert an existing <em>view</em> on the
* formerly {@linkplain ObjectStatus#UNREACHABLE UNREACHABLE} object to a view on the newly discovered
* {@linkplain ObjectStatus#DARK DARK} matter that replaced it (or should it be converted to a dead object view?).</li>
* </ul></li>
* <li>At the end of the {@linkplain HeapPhase#RECLAIMING reclaiming} phase, every {@linkplain ObjectStatus#UNREACHABLE UNREACHABLE}
* object in the Object Map should have become {@linkplain ObjectStatus#DEAD DEAD}: overwritten in memory by a
* {@linkplain ObjectStatus#FREE FREE} quasi-object or a {@linkplain ObjectStatus#DARK DARK} quasi-object.</li>
* <li>No other changes to the Object Map take place during the {@linkplain HeapPhase#RECLAIMING reclaiming} phase.</li>
* </ol></li>
* <li><b>Free Space Map:</b>
* <ol>
* <li>The Free Space map contains only {@linkplain ObjectStatus#FREE FREE}
* or {@linkplain ObjectStatus#DARK DARK} quasi-object references.</li>
* <li>Previously unseen {@linkplain ObjectStatus#FREE FREE} quasi-objects may be discovered
* and added to the Free Space Map, but they do not change during this phase.</li>
* <li>Previously unseen {@linkplain ObjectStatus#DARK DARK} quasi-objects may be discovered and added to the
* Free Space Map, but they do not change during this phase.</li>
* <li>A {@linkplain ObjectStatus#FREE FREE} quasi-object reference in the Free Space Map may be discovered, by observing that its hub pointer
* has changed, to have been <em>overwritten</em> through merging with another {@linkplain ObjectStatus#FREE FREE} quasi-object. This reference becomes
* {@linkplain ObjectStatus#DEAD DEAD} and is removed from the Free Space Map.</li>
* <li>A {@linkplain ObjectStatus#FREE FREE} quasi-object reference in the Free Space Map may be discovered to have changed in size through
* consolidation with following reclaimed space.</li>
* <li>A {@linkplain ObjectStatus#DARK DARK} object in the Free Space Map maybe be discovered to have
* been <em>overwritten</em> through merging with another {@linkplain ObjectStatus#FREE FREE} quasi-object. This quasi-reference becomes
* {@linkplain ObjectStatus#DEAD DEAD} and is removed from the FreeSpace Map.</li>
* </ol></li></ul>
* <p>
* @see MSHeapScheme
* @see HeapFreeChunk
* @see DarkMatter
* @see DarkMatter.SmallestDarkMatter
* @see Memory#ZAPPED_MARKER
* @see Sweeper#minReclaimableSpace()
* @see VmMarkBitmap
* @see MSRemoteReference
*/
public final class RemoteMSHeapScheme extends AbstractRemoteHeapScheme implements RemoteObjectReferenceManager, VmMarkBitmapHeap {
private static final int TRACE_VALUE = 1;
private final TimedTrace heapUpdateTracer;
private long lastUpdateEpoch = -1L;
private long lastGCCompletedCount = 0L;
private long lastReclaimingPhaseCount = 0L;
/**
* The VM object that implements the {@link HeapScheme} in the current configuration.
*/
private TeleMSHeapScheme scheme;
/**
* The VM object that describes the location of the collector's Object-Space.
*/
private TeleContiguousHeapSpace objectSpaceMemoryRegion = null;
/**
* Map: VM address in Object-Space --> a {@link MSRemoteReference} that refers to the object whose origin is at that location.
* <p>
* <strong>Invariant</strong>: the map holds only objects with status {@linkplain ObjectStatus#LIVE LIVE} or {@linkplain ObjectStatus#UNREACHABLE UNREACHABLE}
*/
private WeakRemoteReferenceMap<MSRemoteReference> objectRefMap = new WeakRemoteReferenceMap<MSRemoteReference>();
/**
* Map: VM address in Object-Space --> a {@link MSRemoteReference} that refers to the free space chunk whose origin is at that location.
* <p>
* <strong>Invariant</strong>: the map holds only objects with status {@linkplain ObjectStatus#FREE FREE} or {@linkplain ObjectStatus#DARK DARK}.
*/
private WeakRemoteReferenceMap<MSRemoteReference> freeSpaceRefMap = new WeakRemoteReferenceMap<MSRemoteReference>();
private final List<VmHeapRegion> heapRegions = new ArrayList<VmHeapRegion>(1);
protected RemoteMSHeapScheme(TeleVM vm) {
super(vm);
this.heapUpdateTracer = new TimedTrace(TRACE_VALUE, tracePrefix() + "updating");
if (!vm.bootImage().vmConfiguration.debugging()) {
final StringBuilder sb = new StringBuilder();
sb.append(tracePrefix());
sb.append(": specialized inspector heap support for " + heapSchemeClass().getSimpleName());
sb.append(" will not work correctly; DEBUG boot image required");
TeleWarning.message(sb.toString());
}
// TODO (mlvdv) handle attach mode, where the memory region will already be allocated and we need to know about it right away
}
public Class heapSchemeClass() {
return MSHeapScheme.class;
}
/**
* {@inheritDoc}
* <p>
* Include the array of longs that holds the mark bitmap data.
*/
@Override
public List<MaxObject> inspectableObjects() {
List<MaxObject> inspectableObjects = new ArrayList<MaxObject>();
inspectableObjects.addAll(super.inspectableObjects());
if (scheme.markBitmap != null) {
final MaxObject representation = scheme.markBitmap.representation();
if (representation != null) {
inspectableObjects.add(representation);
}
}
return inspectableObjects;
}
public void initialize(long epoch) {
vm().addInitializationListener(new InitializationListener() {
public void initialiationComplete(final long initializationEpoch) {
objects().registerTeleObjectType(MSHeapScheme.class, TeleMSHeapScheme.class);
// Get the VM object that represents the heap implementation; can't do this any sooner during startup.
scheme = (TeleMSHeapScheme) teleHeapScheme();
assert scheme != null;
updateMemoryStatus(initializationEpoch);
/*
* Add a heap phase listener that will will force the VM to stop any time the heap transitions from
* analysis to the RECLAIMING phase of a GC. This is exactly the moment in a GC cycle when reference
* information must be updated. Unfortunately, the handler supplied with this listener will only be
* called after the VM state refresh cycle is complete. That would be too late since so many other parts
* of the refresh cycle depend on references. Consequently, the needed updates take place when this
* manager gets refreshed (early in the refresh cycle, see UpdateMemoryStatus()), not when this handler
* eventually gets called.
*/
try {
vm().addGCPhaseListener(RECLAIMING, new MaxGCPhaseListener() {
public void gcPhaseChange(HeapPhase phase) {
// Dummy handler; the actual updates must be done early during the refresh cycle.
final long phaseChangeEpoch = vm().teleProcess().epoch();
Trace.line(TRACE_VALUE, tracePrefix() + " VM stopped for reference updates, epoch=" + phaseChangeEpoch + ", gc cycle=" + gcStartedCount());
Trace.line(TRACE_VALUE, tracePrefix() + " Note: updates have long since been done by the time this (dummy) handler is called");
}
});
} catch (MaxVMBusyException e) {
TeleError.unexpected(tracePrefix() + "Unable to add GC Phase Listener");
}
}
});
}
public List<VmHeapRegion> heapRegions() {
return heapRegions;
}
// TODO (mlvdv) Consider whether we should periodically purge the maps of weak references to references that have been collected.
/**
* {@inheritDoc}
* <p>
* This gets called more than once during the startup sequence.
* <p>
* The sequence of things to be updated is based on an analysis of what might have happened since the last we
* checked. In the limiting case, since the only forced halt is at the beginning of the
* {@linkplain HeapPhase#RECLAIMING RECLAIMING} phase, we might find that we have halted at this point in the cycle
* and we haven't updated since the last halt at the same point.
*/
@Override
public void updateMemoryStatus(long epoch) {
super.updateMemoryStatus(epoch);
updateFreeHubOrigins();
if (scheme == null) {
// Can't do anything until we have the VM object that represents the scheme implementation
return;
}
// Ensure we have the latest information from the remote scheme object, since it may not yet have been touched in the refresh cycle.
scheme.updateCacheIfNeeded();
if (objectSpaceMemoryRegion == null) {
Trace.begin(TRACE_VALUE, tracePrefix() + "looking for heap region");
/*
* The heap region has not yet been discovered. Don't check the epoch, since this check may need to be run
* more than once during the startup sequence, as the information needed for access to the information is
* incrementally established. Information about the region, other then location need not be checked once
* established.
*/
objectSpaceMemoryRegion = scheme.objectSpace.contiguousHeapSpace();
if (objectSpaceMemoryRegion != null) {
final VmHeapRegion vmHeapRegion = new VmHeapRegion(vm(), objectSpaceMemoryRegion, this);
heapRegions.add(vmHeapRegion);
vm().addressSpace().add(vmHeapRegion.memoryRegion());
}
Trace.end(TRACE_VALUE, tracePrefix() + "looking for heap region: " + (heapRegions.isEmpty() ? "not" : "") + " found");
}
if (objectSpaceMemoryRegion != null && epoch > lastUpdateEpoch) {
heapUpdateTracer.begin();
/*
* This is a normal refresh. Immediately update information about the location/size/allocation of the heap
* region, which is represented by a remote object; this update must be forced because remote objects are
* otherwise not refreshed until later in the update cycle.
*/
objectSpaceMemoryRegion.updateCache(epoch);
// The order of the following reference updates is significant
if (lastGCCompletedCount < gcCompletedCount()) {
/*
* A GC, and in particular a RECLAIMING phase, has completed since the last time we checked. Find any
* references still marked UNREACHABLE and make them DEAD. It is important to clear them out before we
* handle the possible conclusion of the following ANALYZING phase that may also have happened since
* we last checked.
*/
int unreachable = 0;
for (MSRemoteReference objectRef : objectRefMap.values()) {
if (objectRef.status().isUnreachable()) {
objectRef.die();
assert objectRefMap.remove(objectRef.origin()) != null;
unreachable++;
}
}
if (unreachable > 0) {
Trace.line(TRACE_VALUE, tracePrefix() + "first halt after GC cycle=" + gcCompletedCount() + ", unreachable died=" + unreachable);
}
lastGCCompletedCount = gcCompletedCount();
}
if (phase().isReclaiming()) {
if (lastReclaimingPhaseCount < gcStartedCount()) {
/*
* We are halted for the first time in this RECLAIMING phase, usually because of the phase change
* listener we registered. That means that an ANALYZING phase has concluded since the last time we
* checked. Find any LIVE object references that are unmarked and make them UNREACHABLE before
* anything else happens during this phase.
*/
int reachable = 0;
int unreachable = 0;
for (MSRemoteReference objectRef : objectRefMap.values()) {
if (objectRef.status().isLive()) {
if (markBitMap().getMarkColor(objectRef.origin()) != MarkColor.MARK_BLACK) {
objectRef.discoveredUnreachable();
unreachable++;
} else {
reachable++;
}
} else {
// There shouldn't be any UNREACHABLE objects at this point; any lingering ones have been removed by the previous check.
TeleWarning.message(tracePrefix() + "Found unexpected ref status in reclaiming, start=" + objectRef);
}
}
Trace.line(TRACE_VALUE, tracePrefix() + "first halt reclaiming in GC cycle=" + gcStartedCount() + ", reachable=" + reachable + ", unreachable=" + unreachable);
lastReclaimingPhaseCount = gcStartedCount();
}
/*
* Every time we stop while RECLAIMING, check to see if any unreachable objects have been reclaimed
* since we last checked. The memory that held an unreachable object might now hold a FREE chunk, DARK
* matter, or might be entirely zapped.
*/
int unreachableDied = 0;
for (MSRemoteReference objectRef : objectRefMap.values()) {
if (objectRef.status().isUnreachable()) {
final Address origin = objectRef.origin();
if (objectStatusAt(origin).isDead() || isHeapFreeChunkOrigin(origin) || isDarkMatterOrigin(origin)) {
objectRef.die();
assert objectRefMap.remove(objectRef.origin()) != null;
unreachableDied++;
}
}
}
if (unreachableDied > 0) {
Trace.line(TRACE_VALUE, tracePrefix() + "halt reclaiming in GC cycle=" + gcStartedCount() + ", unreachable died=" + unreachableDied);
}
}
/*
* No matter what phase we're in, once everything else is taken care of, see if any free space quasi-objects have been
* overwritten. Do this last because nothing else in this part of the update is affected by it. Free space
* isn't supposed to be overwritten during the ANALYSIS phase, but we might not have checked since some time in
* an earlier phase.
*/
int freeDied = 0;
for (MSRemoteReference freeSpaceRef : freeSpaceRefMap.values()) {
switch (freeSpaceRef.status()) {
case FREE:
// FREE chunks can be overwritten during a MUTATING phase when the space is used to allocate a new object.
// FREE chunks can be overwritten during a RECLAIMING phase when the space is merged with another FREE chunk.
if (!objectStatusAt(freeSpaceRef.origin()).isFree()) {
// The reference no longer points at a free space chunk, so it has been overwritten.
freeSpaceRef.die();
assert freeSpaceRefMap.remove(freeSpaceRef.origin()) != null;
freeDied++;
}
break;
case DARK:
// DARK chunks can be overwritten during a RECLAIMING phase when the space is merged with another FREE chunk.
if (!objectStatusAt(freeSpaceRef.origin()).isDark()) {
// The reference no longer points at dark matter, so it has been overwritten.
freeSpaceRef.die();
assert freeSpaceRefMap.remove(freeSpaceRef.origin()) != null;
freeDied++;
}
break;
}
}
if (freeDied > 0) {
Trace.line(TRACE_VALUE, tracePrefix() + "halt " + phase().name() + " in GC cycle=" + gcStartedCount() + ", free died=" + freeDied);
}
heapUpdateTracer.end(heapUpdateStatsPrinter);
lastUpdateEpoch = epoch;
}
}
// TODO (mlvdv) I took a stab at this; not sure how accurate, especially when collecting
public MaxMemoryManagementInfo getMemoryManagementInfo(final Address address) {
return new MaxMemoryManagementInfo() {
public MaxMemoryManagementStatus status() {
final MaxHeapRegion heapRegion = heap().findHeapRegion(address);
final HeapPhase phase = heap().phase();
if (heapRegion == null) {
// The location is not in any memory region allocated by the heap.
return MaxMemoryManagementStatus.NONE;
}
if (!objectSpaceMemoryRegion.contains(address)) {
// In some other heap region such as boot or immortal; use the simple test
if (heapRegion.memoryRegion().containsInAllocated(address)) {
return MaxMemoryManagementStatus.LIVE;
}
return MaxMemoryManagementStatus.FREE;
}
if (!objectSpaceMemoryRegion.containsInAllocated(address)) {
// In the dynamic heap, but not yet made available for allocation
return MaxMemoryManagementStatus.DEAD;
}
if (phase.isCollecting()) {
// Not sure what can be done here; assume LIVE
return MaxMemoryManagementStatus.LIVE;
}
for (TeleNativeThread teleNativeThread : vm().teleProcess().threads()) { // iterate over threads in check in case of tlabs if objects are dead or live
TeleThreadLocalsArea teleThreadLocalsArea = teleNativeThread.localsBlock().tlaFor(SafepointPoll.State.ENABLED);
if (teleThreadLocalsArea != null) {
Word tlabDisabledWord = teleThreadLocalsArea.getWord(HeapSchemeWithTLAB.TLAB_DISABLED_THREAD_LOCAL_NAME);
Word tlabMarkWord = teleThreadLocalsArea.getWord(HeapSchemeWithTLAB.TLAB_MARK_THREAD_LOCAL_NAME);
Word tlabTopWord = teleThreadLocalsArea.getWord(HeapSchemeWithTLAB.TLAB_TOP_THREAD_LOCAL_NAME);
if (tlabDisabledWord.isNotZero() && tlabMarkWord.isNotZero() && tlabTopWord.isNotZero()) {
if (address.greaterEqual(tlabMarkWord.asAddress()) && tlabTopWord.asAddress().greaterThan(address)) {
return MaxMemoryManagementStatus.FREE;
}
}
}
}
// Everything else should be live.
return MaxMemoryManagementStatus.LIVE;
}
public String terseInfo() {
return "";
}
public String shortDescription() {
return vm().heapScheme().name();
}
public Address address() {
return address;
}
public TeleObject tele() {
return null;
}
};
}
public MaxMarkBitmap markBitMap() {
return scheme.markBitmap;
}
public ObjectStatus objectStatusAt(Address origin) throws TeleError {
TeleError.check(contains(origin), "Location is outside MS heap region");
switch(phase()) {
case MUTATING:
case ANALYZING:
if (objectSpaceMemoryRegion.containsInAllocated(origin) && objects().isPlausibleOriginUnsafe(origin)) {
if (isHeapFreeChunkOrigin(origin)) {
return ObjectStatus.FREE;
}
if (isDarkMatterOrigin(origin)) {
return ObjectStatus.DARK;
}
return ObjectStatus.LIVE;
}
break;
case RECLAIMING:
if (objectSpaceMemoryRegion.containsInAllocated(origin) && objects().isPlausibleOriginUnsafe(origin)) {
if (isHeapFreeChunkOrigin(origin)) {
return ObjectStatus.FREE;
}
if (isDarkMatterOrigin(origin)) {
return ObjectStatus.DARK;
}
switch(scheme.markBitmap.getMarkColorUnsafe(origin)) {
case MARK_BLACK:
return ObjectStatus.LIVE;
case MARK_WHITE:
return ObjectStatus.UNREACHABLE;
case MARK_GRAY:
TeleWarning.message("Gray Mark found during Reclaiming @ :" + origin.to0xHexString());
}
}
break;
default:
TeleError.unknownCase();
}
return ObjectStatus.DEAD;
}
/**
* {@inheritDoc}
* <p>
* The MS collector does not relocate objects.
*/
public boolean isForwardingAddress(Address forwardingAddress) {
return false;
}
public RemoteReference makeReference(Address origin) throws TeleError {
assert vm().lockHeldByCurrentThread();
// It is an error to attempt creating a reference if the address is completely outside the managed region(s).
TeleError.check(contains(origin), "Location is outside MS heap region");
final MSRemoteReference oldRef = objectRefMap.get(origin);
if (oldRef != null) {
// A live object or unreachable quasi-object is in the map at that location; only return if live.
return oldRef.status().isLive() ? oldRef : null;
}
if (freeSpaceRefMap.get(origin) != null) {
// A reference to a free space quasi object at that address already exists; nothing LIVE here.
return null;
}
// Not in either map; might be a live object not yet seen.
if (objectStatusAt(origin).isLive()) {
final MSRemoteReference newLiveRef = MSRemoteReference.createLive(this, origin);
if (newLiveRef != null) {
objectRefMap.put(origin, newLiveRef);
return newLiveRef;
}
}
return null;
}
public RemoteReference makeQuasiReference(Address origin) throws TeleError {
assert vm().lockHeldByCurrentThread();
// It is an error to attempt creating a reference if the address is completely outside the managed region(s).
TeleError.check(contains(origin), "Location is outside MS heap region");
final MSRemoteReference oldFreeSpaceRef = freeSpaceRefMap.get(origin);
if (oldFreeSpaceRef != null) {
// A reference to some kind of quasi-object is already in the map
return oldFreeSpaceRef;
}
final MSRemoteReference oldObjectRef = objectRefMap.get(origin);
if (oldObjectRef != null) {
// A live object or unreachable quasi-object is in the map at that location; only return if unreachable.
return oldObjectRef.status().isUnreachable() ? oldObjectRef : null;
}
// Not in either map; might be a quasi-object not yet seen.
switch (objectStatusAt(origin)) {
case FREE:
final MSRemoteReference newFreeRef = MSRemoteReference.createFree(this, origin);
freeSpaceRefMap.put(origin, newFreeRef);
return newFreeRef;
case DARK:
final MSRemoteReference newDarkRef = MSRemoteReference.createDark(this, origin);
freeSpaceRefMap.put(origin, newDarkRef);
break;
case UNREACHABLE:
final MSRemoteReference newUnreachableRef = MSRemoteReference.createUnreachable(this, origin);
objectRefMap.put(origin, newUnreachableRef);
return newUnreachableRef;
default:
TeleError.unexpected();
}
return null;
}
/**
* Does the heap region contain the address anywhere (allocated or not)?
*/
private boolean contains(Address address) {
return objectSpaceMemoryRegion != null && objectSpaceMemoryRegion.contains(address);
}
/**
* Is the address in an area where an object could be?
*/
private boolean inLiveArea(Address address) {
// TODO (mlvdv) refine
return objectSpaceMemoryRegion.containsInAllocated(address);
}
public void printObjectSessionStats(PrintStream printStream, int indent, boolean verbose) {
if (objectSpaceMemoryRegion != null) {
final NumberFormat formatter = NumberFormat.getInstance();
int liveRefs = 0;
int unreachableRefs = 0;
int freeRefs = 0;
int darkRefs = 0;
int deadRefs = 0;
for (MSRemoteReference ref : objectRefMap.values()) {
switch(ref.status()) {
case LIVE:
liveRefs++;
break;
case UNREACHABLE:
unreachableRefs++;
break;
case FREE:
freeRefs++;
break;
case DARK:
darkRefs++;
break;
case DEAD:
deadRefs++;
break;
}
}
final int totalRefs = liveRefs + unreachableRefs + freeRefs + darkRefs + deadRefs;
// Line 0
String indentation = Strings.times(' ', indent);
final StringBuilder sb0 = new StringBuilder();
sb0.append("Dynamic Heap:");
if (verbose) {
sb0.append(" VMScheme=").append(vm().heapScheme().name());
sb0.append(", ref. mgr=").append(getClass().getSimpleName());
}
printStream.println(indentation + sb0.toString());
// increase indentation
indentation += Strings.times(' ', 4);
// Line 1
final StringBuilder sb1 = new StringBuilder();
sb1.append("phase=").append(phase().label());
sb1.append(", collections started=").append(formatter.format(gcStartedCount));
sb1.append(", completed=").append(formatter.format(gcCompletedCount));
sb1.append(", total object refs mapped=").append(formatter.format(totalRefs));
printStream.println(indentation + sb1.toString());
// Line 2
final StringBuilder sb11 = new StringBuilder();
sb11.append("memory: ");
final MemoryUsage usage = objectSpaceMemoryRegion.getUsage();
final long size = usage.getCommitted();
if (size > 0) {
sb11.append("size=" + formatter.format(size));
} else {
sb11.append(" <unallocated>");
}
printStream.println(indentation + sb11.toString());
// Line 3, optional
if (totalRefs > 0) {
final StringBuilder sb2 = new StringBuilder();
sb2.append("objects: ");
sb2.append(ObjectStatus.LIVE.label()).append("=").append(formatter.format(liveRefs)).append(", ");
sb2.append(ObjectStatus.UNREACHABLE.label()).append("=").append(formatter.format(unreachableRefs)).append(", ");
sb2.append(ObjectStatus.FREE.label()).append("=").append(formatter.format(freeRefs)).append(", ");
sb2.append(ObjectStatus.DARK.label()).append("=").append(formatter.format(darkRefs));
printStream.println(indentation + sb2.toString());
}
if (deadRefs > 0) {
printStream.println(indentation + "ERROR: " + formatter.format(deadRefs) + " DEAD refs in map");
}
}
}
/**
* Surrogate object for the scheme instance in the VM.
*/
public static class TeleMSHeapScheme extends TeleHeapScheme {
private TeleFreeHeapSpaceManager objectSpace;
private TeleTricolorHeapMarker remoteHeapMarker;
private VmMarkBitmap markBitmap = null;
public TeleMSHeapScheme(TeleVM vm, RemoteReference reference) {
super(vm, reference);
}
@Override
protected boolean updateObjectCache(long epoch, StatsPrinter statsPrinter) {
if (!super.updateObjectCache(epoch, statsPrinter)) {
return false;
}
if (objectSpace == null) {
final RemoteReference freeHeapSpaceManagerRef = fields().MSHeapScheme_objectSpace.readRemoteReference(reference());
objectSpace = (TeleFreeHeapSpaceManager) objects().makeTeleObject(freeHeapSpaceManagerRef);
} else {
objectSpace.updateCacheIfNeeded();
}
if (remoteHeapMarker == null) {
// Allocated in boot heap, so it exists the first time we check.
final RemoteReference heapMarkerRef = fields().MSHeapScheme_heapMarker.readRemoteReference(reference());
remoteHeapMarker = (TeleTricolorHeapMarker) objects().makeTeleObject(heapMarkerRef);
} else {
remoteHeapMarker.updateCacheIfNeeded();
}
// assert remoteHeapmarker != null
if (markBitmap == null && remoteHeapMarker.isAllocated()) {
markBitmap = new VmMarkBitmap(vm(), remoteHeapMarker);
vm().addressSpace().add(markBitmap.memoryRegion());
}
return true;
}
}
}