/*
* 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.util.*;
import com.sun.max.memory.*;
import com.sun.max.program.*;
import com.sun.max.tele.*;
import com.sun.max.tele.TeleVM.InitializationListener;
import com.sun.max.tele.field.*;
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.gen.semispace.*;
import com.sun.max.tele.util.*;
import com.sun.max.unsafe.*;
import com.sun.max.vm.heap.*;
import com.sun.max.vm.heap.sequential.gen.semiSpace.*;
/**
* Inspector support for working with VM sessions using the VM's simple
* {@linkplain GenSSHeapScheme generational collector},
* an implementation of the VM's {@link HeapScheme} interface.
* WORK IN PROGRESS.
*/
public final class RemoteGenSSHeapScheme extends AbstractRemoteHeapScheme implements RemoteObjectReferenceManager, VmCardTableHeap, VmRelocatingHeap {
private static final int TRACE_VALUE = 1;
/**
* Number of contiguous VM regions for dynamic heap space.
*/
private static final int NUM_DYN_HEAP_REGIONS = 3;
/**
* Total number of VM regions for the heap. This adds to the regions of the dynamic heap the region for the remembered set (card table and FOT).
*/
private static final int MAX_VM_HEAP_REGIONS = NUM_DYN_HEAP_REGIONS + 1;
private TeleGenSSHeapScheme scheme;
private final List<VmHeapRegion> heapRegions = new ArrayList<VmHeapRegion>(MAX_VM_HEAP_REGIONS);
private final TimedTrace heapUpdateTracer;
private long lastUpdateEpoch = -1L;
private long lastAnalyzingPhaseCount = 0L;
private long lastReclaimingPhaseCount = 0L;
/**
* Track full completed collection count. Updated only when entering the reclaiming phase of a full GC.
*/
private long lastCompletedFullCollectionCount = 0L;
/**
* Indicates whether the current {@link HeapPhase#ANALYZING} or {@link HeapPhase#RECLAIMING} phases are for an old generation collection.
*/
private boolean isFullGC = false;
/**
* Indicates whether the current {@link HeapPhase#MUTATING} phase is a non-allocating transition between minor and full collection.
* If set while in the {@link HeapPhase#MUTATING} phase, the young generation doesn't hold any live objects and there should be no live reference to young generation addresses.
*/
private boolean requiresFullGC;
private boolean initialized = false;
private void updateFullGCStatus() {
int fullCollectionCount = scheme.fullCollectionCount();
isFullGC = fullCollectionCount > lastCompletedFullCollectionCount;
}
/**
* Helper class to describe live ranges of old objects produced by an evacuation.
* Only two instances: one for evacuation of the young generation, the other for evacuation
* of the old generation.
* In both case, objects are evacuated in the old to-space and overflow may occurs.
* On overflow, objects are evacuated in an overflow area.
* The overflow area is the old from space for young generation evacuations, and the young generation
* for old generation evacuation.
* If no overflow occurred, survivors resides in one range defined by the initial evacuation mark and the top of the evacuation's allocator.
* If overflow occurred, survivors resides in two ranges: a range in the old-to space between the initial evacuation mark and the overflow mark,
* and the range in the overflow area between the start and top of the old space allocator (now set to the overflow area).
*/
final class SurvivorRangesInfo {
final MemoryRegion survivorRange = new MemoryRegion();
final MemoryRegion overflowRange = new MemoryRegion();
final RemoteReference evacuator;
boolean overflowOccurred;
boolean updated = false;
SurvivorRangesInfo(RemoteReference evacuator) {
this.evacuator = evacuator;
}
private void checkValidUse() {
assert updated;
}
private boolean requiresUpdate() {
return
(phase() == ANALYZING && (isFullGC ? this == oldSurvivorRanges : this == youngSurvivorRanges)) ||
(phase() == MUTATING && requiresFullGC && this == youngSurvivorRanges) ||
(phase() == RECLAIMING && (isFullGC ? this == oldSurvivorRanges : this == youngSurvivorRanges));
}
void updateRanges(boolean evacuationOverflow, Address overflowEvacuationMark) {
if (!requiresUpdate()) {
return;
}
this.overflowOccurred = evacuationOverflow;
survivorRange.setStart(scheme.initialEvacuationMark(evacuator));
Address oldAllocatorTop = phase() == ANALYZING ? scheme.ptop(evacuator) : overflowArea.getRegionEnd();
if (overflowOccurred) {
survivorRange.setEnd(overflowEvacuationMark);
overflowRange.setStart(oldAllocator.start());
overflowRange.setEnd(oldAllocatorTop);
} else {
survivorRange.setEnd(oldAllocatorTop);
overflowRange.setStart(Address.zero());
overflowRange.setSize(Size.zero());
}
updated = true;
}
boolean inSurvivorRanges(Address origin) {
checkValidUse();
return survivorRange.contains(origin) || overflowRange.contains(origin);
}
boolean isPlausibleSurvivor(Address origin) {
return inSurvivorRanges(origin) && objects().isPlausibleOriginUnsafe(origin);
}
boolean isPlausibleForwardingAddress(Address forwardingAddress) {
if (inSurvivorRanges(forwardingAddress)) {
Address possibleOrigin = objects().forwardingPointerToOriginUnsafe(forwardingAddress);
return possibleOrigin.isNotZero() && objectStatusAt(possibleOrigin).isLive();
}
return false;
}
boolean inOldRanges(Address origin) {
checkValidUse();
if (overflowOccurred) {
return (origin.greaterEqual(oldTo.getRegionStart()) && origin.lessThan(survivorRange.end())) || overflowRange.contains(origin);
} else {
return oldAllocator.containsInAllocated(origin);
}
}
boolean isPlausibleOld(Address origin) {
return inOldRanges(origin) && objects().isPlausibleOriginUnsafe(origin);
}
}
/**
* Describes live ranges produced by a young generation evacuation.
* Updated once per update cycle that requires its uses as described below.
* It is used during minor collections, in {@link HeapPhase#ANALYZING} to identify promoted objects and their forwarders,
* and in {@link HeapPhase#RECLAIMING} to identify live old objects.
* If the collection upgrade to full collection it is also used during the fake {@link HeapPhase#MUTATING} phase
* between the young and old generation collections.
*/
SurvivorRangesInfo youngSurvivorRanges;
/**
* Updated once per update cycle that requires its uses as described below.
* Updated once per update cycle.
* Used during old generation collection, in {@link HeapPhase#ANALYZING} to identify old surviving objects and their forwarders,
* in {@link HeapPhase#RECLAIMING} to identify live objects.
*/
SurvivorRangesInfo oldSurvivorRanges;
private TeleContiguousHeapSpace nursery;
private TeleContiguousHeapSpace oldFrom;
private TeleContiguousHeapSpace oldTo;
private TeleMemoryRegion overflowArea;
private TeleCardTableRSet cardTableRSet;
private TeleBaseAtomicBumpPointerAllocator nurseryAllocator;
private TeleBaseAtomicBumpPointerAllocator oldAllocator;
/**
* Access to the card table for external use and visualization.
*/
private VmCardTable vmCardTable;
/**
* Track whether a minor evacuation overflowed into from space.
*/
boolean minorEvacuationOverflow = false;
/**
* Track whether a old evacuation overflowed into young space.
*/
boolean oldEvacuationOverflow = false;
/**
* Map VM addresses in the nursery to {@link GenSSRemoteReference} that refer to the object whose origin is at that location, except
* for addresses corresponding to overflowed old evacuation survivors when in full GC {@link HeapPhase#ANALYZING}.
* Reference in this map can be in either of these states: {@linkplain GenSSRemoteReference.RefState#YOUNG_REF_LIVE},
* {@linkplain GenSSRemoteReference.RefState#YOUNG_FORWARDER}, {@linkplain GenSSRemoteReference.RefState#YOUNG_REF_FROM}.
*/
private WeakRemoteReferenceMap<GenSSRemoteReference> nurseryRefMap = new WeakRemoteReferenceMap<GenSSRemoteReference>();
/**
* Map VM addresses in the To-Space of the old generation to {@link GenSSRemoteReference} that refer to objects whose origin is at that location.
*/
private WeakRemoteReferenceMap<GenSSRemoteReference> oldToSpaceRefMap = new WeakRemoteReferenceMap<GenSSRemoteReference>();
/**
* Map VM addresses in the From-Space of the old generation to {@link GenSSRemoteReference} that refer to objects whose origin is at that location.
*/
private WeakRemoteReferenceMap<GenSSRemoteReference> oldFromSpaceRefMap = new WeakRemoteReferenceMap<GenSSRemoteReference>();
/**
* Map VM addresses in the promotion area of the old generation to {@link GenSSRemoteReference} that refer to objects whose origin is at that location.
*/
private WeakRemoteReferenceMap<GenSSRemoteReference> promotedRefMap = new WeakRemoteReferenceMap<GenSSRemoteReference>();
/**
* Map VM addresses to unallocated space. These may be heap free chunk or dark matter.
* <p>
* <strong>Invariant</strong>: the map holds only objects with status {@linkplain ObjectStatus#FREE FREE} or {@linkplain ObjectStatus#DARK DARK}.
*/
private WeakRemoteReferenceMap<GenSSRemoteReference> unallocatedRefMap = new WeakRemoteReferenceMap<GenSSRemoteReference>();
final private boolean isDump;
public RemoteGenSSHeapScheme(TeleVM vm) {
super(vm);
this.heapUpdateTracer = new TimedTrace(TRACE_VALUE, tracePrefix() + "updating");
this.isDump = TeleVM.isDump();
}
@Override
public Class heapSchemeClass() {
return GenSSHeapScheme.class;
}
@Override
public void initialize(long epoch) {
vm().addInitializationListener(new InitializationListener() {
public void initialiationComplete(final long initializationEpoch) {
objects().registerTeleObjectType(GenSSHeapScheme.class, TeleGenSSHeapScheme.class);
// Get the VM object that represents the heap implementation; can't do this any sooner during startup.
scheme = (TeleGenSSHeapScheme) teleHeapScheme();
assert scheme != null;
updateMemoryStatus(initializationEpoch);
/*
* Add a heap phase listener that 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), not when this handler eventually gets called.
* The needed manager knows about the systematic stop at this phase transition and take this into account
* when taking action at phase transition.
*/
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");
}
}
});
}
private void addHeapRegion(TeleMemoryRegion memoryRegion) {
if (memoryRegion != null) {
final VmHeapRegion vmHeapRegion = new VmHeapRegion(vm(), memoryRegion, this);
heapRegions.add(vmHeapRegion);
vm().addressSpace().add(vmHeapRegion.memoryRegion());
}
}
private void initializeHeapRegions() {
Trace.begin(TRACE_VALUE, tracePrefix() + "looking for heap regions");
if (nursery == null) {
nursery = scheme.readTeleYoungSpace();
addHeapRegion(nursery);
}
if (oldFrom == null) {
oldFrom = scheme.readTeleOldFromSpace();
addHeapRegion(oldFrom);
}
if (oldTo == null) {
oldTo = scheme.readTeleOldToSpace();
addHeapRegion(oldTo);
}
if (cardTableRSet == null) {
cardTableRSet = scheme.readTeleCardTableRSet();
heapRegions.add(cardTableRSet.vmHeapRegion);
vm().addressSpace().add(cardTableRSet.vmHeapRegion.memoryRegion());
vmCardTable = new VmCardTable(vm(), cardTableRSet);
}
Trace.end(TRACE_VALUE, tracePrefix() + "looking for heap regions, " + heapRegions.size() + " found");
}
private boolean contains(Address address) {
try {
return nursery.contains(address) || oldFrom.contains(address) || oldTo.contains(address);
} catch (NullPointerException enull) {
return (nursery != null && nursery.contains(address)) || (oldFrom != null && oldFrom.contains(address)) || (oldTo != null && oldTo.contains(address));
}
}
private void beginAnalyzing(String action, WeakRemoteReferenceMap<GenSSRemoteReference> refMap) {
final boolean minorCollection = !isFullGC;
final String prefix = tracePrefix() + "first halt in " + (minorCollection ? "MINOR" : "FULL") + " GC cycle=" + gcStartedCount() + ", ";
Trace.begin(TRACE_VALUE, prefix + action);
List<GenSSRemoteReference> references = refMap.values();
for (GenSSRemoteReference ref : references) {
ref.beginAnalyzing(minorCollection);
}
Trace.end(TRACE_VALUE, prefix + "ASSUMED LIVE refs=" + references.size());
}
private void updateForwardedReferences(WeakRemoteReferenceMap<GenSSRemoteReference> fromSpaceMap, WeakRemoteReferenceMap<GenSSRemoteReference> toSpaceMap) {
final boolean minorCollection = !isFullGC;
final String prefix = tracePrefix() + "checking forwarding refs, " + (minorCollection ? "MINOR" : "FULL") + " GC cycle=" + gcStartedCount();
int newlyForwarded = 0;
int live = 0;
Trace.begin(TRACE_VALUE, prefix);
for (GenSSRemoteReference ref : fromSpaceMap.values()) {
switch(ref.status()) {
case FORWARDER:
break;
case LIVE:
final Address origin = ref.origin();
if (origin.isZero()) {
break;
}
if (objects().hasForwardingAddressUnsafe(origin)) {
// A From-Space reference (either to the nursery if doing minor collection, or to the old from-space if doing full collection) has been forwarded since the last time we looked.
final Address toOrigin = objects().getForwardingAddressUnsafe(origin);
// We need to:
// 1. remove the reference from the from space map
fromSpaceMap.remove(origin);
// 2. transition it to a forwarded state
ref.discoverForwarded(toOrigin, minorCollection);
// 3. move it to the to-space map (i.e., either the promoted map or the old to space map).
toSpaceMap.put(toOrigin, ref);
// 4. create a forwarder reference. We currently enter it in the fromSpaceMap. A better approach might be to move it to a dedicated forwarder map so
// these updatedForwardReference only iterate over unforwarded from reference.
fromSpaceMap.put(origin, GenSSRemoteReference.createForwarder(this, ref));
newlyForwarded++;
}
live++;
break;
case DEAD:
TeleError.unexpected(tracePrefix() + "DEAD reference found in From-Space map");
break;
default:
TeleError.unknownCase();
}
}
Trace.end(TRACE_VALUE, prefix + " forwarded=" + newlyForwarded + "(before=" + live + ", after=" + (live + newlyForwarded) + ")");
}
private void updatedReclaimedReference(WeakRemoteReferenceMap<GenSSRemoteReference> fromSpaceMap, WeakRemoteReferenceMap<GenSSRemoteReference> toSpaceMap) {
final boolean minorCollection = !isFullGC;
final String prefix = tracePrefix() + "first halt in RECLAIMING phase of " + (minorCollection ? "MINOR" : "FULL") + " GC cycle=" + gcStartedCount();
Trace.begin(TRACE_VALUE, prefix + " -- clear forwarded & remove dead references");
for (GenSSRemoteReference ref : toSpaceMap.values()) {
switch (ref.status()) {
case LIVE:
ref.endAnalyzing(minorCollection);
break;
case FORWARDER:
TeleError.unexpected(tracePrefix() + "FORWARDER reference found in " + (minorCollection ? "promoted space" : "old to-space") + " map");
break;
case DEAD:
TeleError.unexpected(tracePrefix() + "DEAD reference found in " + (minorCollection ? "promoted space" : "old to-space") + " map");
break;
default:
TeleError.unknownCase();
}
}
int died = 0;
int forwarded = 0;
for (GenSSRemoteReference ref : fromSpaceMap.values()) {
switch (ref.status()) {
case LIVE:
ref.endAnalyzing(minorCollection);
died++;
break;
case FORWARDER:
ref.endAnalyzing(minorCollection);
forwarded++;
break;
case DEAD:
TeleError.unexpected(tracePrefix() + "DEAD reference found in " + (minorCollection ? "nursery" : "old from-space") + " map");
break;
default:
TeleError.unknownCase();
}
}
fromSpaceMap.clear();
Trace.end(TRACE_VALUE, prefix + ", forwarded cleared =" + forwarded + ", died=" + died);
Trace.end(TRACE_VALUE, tracePrefix() + "first halt in GC RECLAIMING, cycle=" + gcStartedCount() + ", reclaimed=(objects=" + died + ", fowarders=" + forwarded + ")");
}
public List<VmHeapRegion> heapRegions() {
return heapRegions;
}
public boolean canCreateLive() {
return !(isFullGC && lastReclaimingPhaseCount < gcStartedCount());
}
public boolean isInOverflowArea(Address origin) {
return overflowArea.contains(origin);
}
private boolean checkNoOverlap(WeakRemoteReferenceMap<GenSSRemoteReference> map1, WeakRemoteReferenceMap<GenSSRemoteReference> map2) {
for (GenSSRemoteReference ref : map1.values()) {
GenSSRemoteReference found = map2.get(ref.toOrigin());
if (found != null) {
System.err.print("ref : " + ref + " duplicated in other map : " + found);
return false;
}
}
return true;
}
@Override
public void updateMemoryStatus(long epoch) {
super.updateMemoryStatus(epoch);
updateFreeHubOrigins();
// Can't do anything until we have the VM object that represents the scheme implementation
if (scheme == null) {
return;
}
if (heapRegions.size() < MAX_VM_HEAP_REGIONS) {
initializeHeapRegions();
if (heapRegions.size() < MAX_VM_HEAP_REGIONS) {
return;
}
nurseryAllocator = scheme.readYoungSpaceAllocator();
oldAllocator = scheme.readOldSpaceAllocator();
overflowArea = scheme.readYoungOverflowArea();
youngSurvivorRanges = new SurvivorRangesInfo(scheme.youngSpaceEvacuator());
oldSurvivorRanges = new SurvivorRangesInfo(scheme.oldSpaceEvacuator());
}
if (epoch > lastUpdateEpoch) {
heapUpdateTracer.begin();
/*
* This is a normal refresh. Immediately update information about the location of the heap regions; this
* update must be forced because remote objects are otherwise not refreshed until later in the update
* cycle.
*/
nursery.updateCache(epoch);
oldTo.updateCache(epoch);
oldFrom.updateCache(epoch);
nurseryAllocator.updateCache(epoch);
oldAllocator.updateCache(epoch);
cardTableRSet.updateCache(epoch);
minorEvacuationOverflow = scheme.minorEvacuationOverflow();
oldEvacuationOverflow = scheme.oldEvacuationOverflow();
requiresFullGC = scheme.requiresFullCollection();
updateFullGCStatus();
// The following two requires the above information, so should be done last.
youngSurvivorRanges.updateRanges(minorEvacuationOverflow, scheme.youngOverflowEvacuationMark());
oldSurvivorRanges.updateRanges(oldEvacuationOverflow, scheme.oldOverflowEvacuationMark());
/*
* For this collector, we only need an overall review of reference state when we're actually collecting.
*/
if (phase().isCollecting()) {
/*
* Check first if a GC cycle has started since the last time we looked.
*/
if (lastAnalyzingPhaseCount < gcStartedCount()) {
assert oldFromSpaceRefMap.isEmpty();
assert promotedRefMap.isEmpty();
// The following isn't relevant is we're inspecting from a core dump.
if (!isDump) {
assert lastAnalyzingPhaseCount == gcStartedCount() - 1;
if (isFullGC) {
final TeleContiguousHeapSpace tempHeapSpace = oldTo;
oldTo = oldFrom;
oldFrom = tempHeapSpace;
final WeakRemoteReferenceMap<GenSSRemoteReference> tempRefMap = oldToSpaceRefMap;
oldToSpaceRefMap = oldFromSpaceRefMap;
oldFromSpaceRefMap = tempRefMap;
if (minorEvacuationOverflow) {
// There might be references in oldFromSpaceRefMap with an origin in to-space after we flipped
// (those that were evacuated in the overflow area).
// We need to move them to the oldToSpaceMap as they are considered live now.
for (GenSSRemoteReference ref : oldFromSpaceRefMap.values()) {
Address origin = ref.origin();
if (overflowArea.contains(origin)) {
oldFromSpaceRefMap.remove(origin);
oldToSpaceRefMap.put(origin, ref);
}
}
}
// Transition the state of all references that are now in the old from-Space
beginAnalyzing("flip old generation semi spaces", oldFromSpaceRefMap);
GenSSRemoteReference.checkNoLiveRef(oldFromSpaceRefMap, false);
} else {
beginAnalyzing("turn all young refs into young from refs ", nurseryRefMap);
}
}
lastAnalyzingPhaseCount = gcStartedCount();
}
/*
* If we're still analyzing, we need to update references to objects that may have been forwarded during this GC cycle.
*/
if (lastReclaimingPhaseCount < gcStartedCount()) {
if (isFullGC) {
updateForwardedReferences(oldFromSpaceRefMap, oldToSpaceRefMap);
GenSSRemoteReference.checkNoLiveRef(oldToSpaceRefMap, false);
} else {
updateForwardedReferences(nurseryRefMap, promotedRefMap);
}
}
if (phase().isReclaiming() && lastReclaimingPhaseCount < gcStartedCount()) {
/*
* The heap is in a GC cycle, and this is the first VM halt during that GC cycle where we know
* analysis is complete. This halt will usually be caused by the special breakpoint we've set at
* entry to the {@linkplain #RECLAIMING} phase. This is the opportunity
* to update reference maps while full information is still available in the collector.
*/
assert lastReclaimingPhaseCount == gcStartedCount() - 1;
lastReclaimingPhaseCount = gcStartedCount();
if (isFullGC) {
updatedReclaimedReference(oldFromSpaceRefMap, oldToSpaceRefMap);
lastCompletedFullCollectionCount = scheme.fullCollectionCount();
} else {
checkNoOverlap(promotedRefMap, oldToSpaceRefMap);
updatedReclaimedReference(nurseryRefMap, promotedRefMap);
checkNoOverlap(promotedRefMap, oldToSpaceRefMap);
for (GenSSRemoteReference ref : promotedRefMap.values()) {
oldToSpaceRefMap.put(ref.toOrigin(), ref);
}
promotedRefMap.clear();
}
}
}
lastUpdateEpoch = epoch;
heapUpdateTracer.end(heapUpdateStatsPrinter);
}
}
@Override
public MaxMemoryManagementInfo getMemoryManagementInfo(final Address address) {
return new MaxMemoryManagementInfo() {
@Override
public MaxMemoryManagementStatus status() {
if (address == null || address.isZero()) {
return MaxMemoryManagementStatus.NONE;
}
final MaxHeapRegion heapRegion = heap().findHeapRegion(address);
if (heapRegion == null) {
// The location is not in any memory region allocated by the heap.
return MaxMemoryManagementStatus.NONE;
}
// TODO (mlvdv) using ObjectStatusAt() isn't correct, but unsure what to do
if (contains(address)) {
switch(objectStatusAt(address)) {
case LIVE:
return MaxMemoryManagementStatus.LIVE;
case FORWARDER:
return MaxMemoryManagementStatus.LIVE;
case DEAD:
return MaxMemoryManagementStatus.DEAD;
case FREE:
return MaxMemoryManagementStatus.FREE;
}
}
return MaxMemoryManagementStatus.LIVE;
}
@Override
public String terseInfo() {
// Return card table index and whether the card is dirty or not.
final int ci = cardTableRSet.cardIndex(address);
return Integer.toString(ci);
}
@Override
public String shortDescription() {
return "Card #";
}
@Override
public Address address() {
return address;
}
@Override
public MaxObject tele() {
return null;
}
};
}
public MaxCardTable cardTable() {
return vmCardTable;
}
public ObjectStatus objectStatusAt(Address origin) {
TeleError.check(contains(origin), "Location is outside GenSSHeapScheme dynamic heap regions");
if (isHeapFreeChunkOrigin(origin)) {
return ObjectStatus.FREE;
}
if (isDarkMatterOrigin(origin)) {
return ObjectStatus.DARK;
}
switch(phase()) {
case MUTATING:
if (requiresFullGC && youngSurvivorRanges.isPlausibleOld(origin)) {
// fake mutating phase between young and old generation collections of a full GC.
return ObjectStatus.LIVE;
} else if ((nurseryAllocator.containsInAllocated(origin) || oldAllocator.containsInAllocated(origin)) &&
objects().isPlausibleOriginUnsafe(origin)) {
// standard MUTATING phase.
return ObjectStatus.LIVE;
}
break;
case ANALYZING:
if (isFullGC) {
if (oldSurvivorRanges.isPlausibleSurvivor(origin)) {
// a survivor old object
return ObjectStatus.LIVE;
}
if (oldFrom.containsInAllocated(origin)) {
if (objects().hasForwardingAddressUnsafe(origin)) {
final Address forwardAddress = objects().getForwardingAddressUnsafe(origin);
if (oldSurvivorRanges.isPlausibleSurvivor(forwardAddress)) {
// a forwarder to a surviving old object
return ObjectStatus.FORWARDER;
}
} else if (objects().isPlausibleOriginUnsafe(origin)) {
// an old from object
return ObjectStatus.LIVE;
}
}
} else if (nursery.containsInAllocated(origin)) {
if (objects().hasForwardingAddressUnsafe(origin)) {
final Address forwardAddress = objects().getForwardingAddressUnsafe(origin);
if (youngSurvivorRanges.isPlausibleSurvivor(forwardAddress)) {
// a forwarder to a promoted object
return ObjectStatus.FORWARDER;
}
} else if (objects().isPlausibleOriginUnsafe(origin)) {
// a young object
return ObjectStatus.LIVE;
}
} else if (youngSurvivorRanges.isPlausibleSurvivor(origin)) {
// A promoted young object
return ObjectStatus.LIVE;
} else if (youngSurvivorRanges.isPlausibleOld(origin)) {
// An old live object
return ObjectStatus.LIVE;
}
break;
case RECLAIMING:
SurvivorRangesInfo survivorRanges = isFullGC ? oldSurvivorRanges : youngSurvivorRanges;
if (survivorRanges.isPlausibleOld(origin)) {
return ObjectStatus.LIVE;
}
break;
default:
TeleError.unknownCase();
}
return ObjectStatus.DEAD;
}
public boolean isForwardingAddress(Address forwardingAddress) {
if (phase() == HeapPhase.ANALYZING) {
if (isFullGC) {
return oldSurvivorRanges.isPlausibleForwardingAddress(forwardingAddress);
} else {
return youngSurvivorRanges.isPlausibleForwardingAddress(forwardingAddress);
}
}
return false;
}
public RemoteReference makeReference(Address origin) throws TeleError {
assert vm().lockHeldByCurrentThread();
TeleError.check(contains(origin), "Location is outside of " + heapSchemeClass().getSimpleName() + " heap");
final RemoteReference reference = internalMakeRef(origin);
if (reference == null) {
return null;
}
if (reference.status().isLive()) {
return reference;
}
return reference.status().isForwarder() && phase() == HeapPhase.ANALYZING ? reference : null;
}
public RemoteReference makeQuasiReference(Address origin) throws TeleError {
assert vm().lockHeldByCurrentThread();
TeleError.check(contains(origin), "Location is outside of " + heapSchemeClass().getSimpleName() + " heap");
final RemoteReference reference = internalMakeRef(origin);
return reference != null && reference.status().isQuasi() ? reference : null;
}
/**
* Creates a reference of the appropriate kind if there is an object or <em>quasi</em>
* object at the specified origin in VM memory.
*
* @param origin an absolute memory location in the VM.
* @return a remote reference to an object in the VM, {@code null} if no object
* @throws TeleError if the origin is not in the memory regions being managed or if invariant to the reference model are violated.
*/
private RemoteReference internalMakeRef(Address origin) {
GenSSRemoteReference ref = null;
switch(phase()) {
case MUTATING:
WeakRemoteReferenceMap<GenSSRemoteReference> map = null;
if (requiresFullGC) {
if (youngSurvivorRanges.inOldRanges(origin)) {
map = oldToSpaceRefMap;
} else {
// not a valid object reference.
break;
}
} else if (nurseryAllocator.containsInAllocated(origin)) {
map = nurseryRefMap;
} else if (oldAllocator.containsInAllocated(origin)) {
map = oldToSpaceRefMap;
} else {
// not a valid object reference.
break;
}
assert map != null;
ref = map.get(origin);
if (ref != null) {
TeleError.check(ref.status().isLive());
} else if (objects().isPlausibleOriginUnsafe(origin)) {
ref = GenSSRemoteReference.createLive(this, origin, true);
map.put(origin, ref);
}
// Otherwise, not a valid object reference.
break;
case RECLAIMING:
// We are either in RECLAIMING phase and there are no young objects (except for old evacuation overflow)
SurvivorRangesInfo survivorRanges = isFullGC ? oldSurvivorRanges : youngSurvivorRanges;
if (survivorRanges.isPlausibleOld(origin)) {
ref = oldToSpaceRefMap.get(origin);
if (ref != null) {
// A reference to the object is already in one of the live map.
TeleError.check(ref.status().isLive());
} else if (objects().isPlausibleOriginUnsafe(origin)) {
ref = GenSSRemoteReference.createLive(this, origin, false);
oldToSpaceRefMap.put(origin, ref);
}
}
// Otherwise, reference is unknown.
break;
case ANALYZING:
if (isFullGC) {
// Full GC. Nursery is empty and only used as overflow area. The nursery ref map must be left empty.
if (oldSurvivorRanges.inSurvivorRanges(origin)) {
ref = oldToSpaceRefMap.get(origin);
if (ref != null) {
// A reference to the object is already in one of the live map.
TeleError.check(ref.status().isLive());
} else if (objects().isPlausibleOriginUnsafe(origin)) {
// A newly discovered object in the old To-Space. In the analyzing phase of a full GC, the object must be a
// copy of a forwarded object.
ref = GenSSRemoteReference.createOldTo(this, origin, false);
oldToSpaceRefMap.put(origin, ref);
}
} else if (oldFrom.containsInAllocated(origin)) {
ref = makeForwardedReference(origin, oldFromSpaceRefMap, oldToSpaceRefMap, false);
}
} else {
if (nursery.containsInAllocated(origin)) {
ref = makeForwardedReference(origin, nurseryRefMap, promotedRefMap, true);
} else if (youngSurvivorRanges.inSurvivorRanges(origin)) {
ref = promotedRefMap.get(origin);
if (ref != null) {
TeleError.check(ref.status().isLive() && !ref.status().isForwarder());
} else if (objects().isPlausibleOriginUnsafe(origin)) {
ref = GenSSRemoteReference.createOldTo(this, origin, true);
promotedRefMap.put(origin, ref);
}
} else if (youngSurvivorRanges.inOldRanges(origin)) {
ref = oldToSpaceRefMap.get(origin);
if (ref != null) {
// A reference to the object is already in one of the live map.
TeleError.check(ref.status().isLive() && !ref.status().isForwarder());
} else if (objects().isPlausibleOriginUnsafe(origin)) {
ref = GenSSRemoteReference.createLive(this, origin, false);
oldToSpaceRefMap.put(origin, ref);
}
}
}
break;
default:
TeleError.unknownCase();
}
return ref == null ? vm().referenceManager().zeroReference() : ref;
}
/**
* Implement the logic for making a forwarding reference. The logic is the same for minor and full collection and is parameterized with
* just the from and to space reference maps used.
* The map for the from space is queried, and a new reference is created if none is found.
* The existing reference may be updated if the status of the object at the specified origin has changed (e.g., it has become a forwarder), and the maps may be
* updated accordingly.
*
* @param fromOrigin the address in the From Space
* @param fromRefMap the map holding the reference to objects of the space being evacuated (either the nursery or the old From space).
* @param toRefMap the map holding the reference to evacuated objects (either the promoted map, or the old To space map)
* @param isMinorCollection true if this is minor collection
* @return the reference corresponding to the origin.
*/
private GenSSRemoteReference makeForwardedReference(
Address fromOrigin,
WeakRemoteReferenceMap<GenSSRemoteReference> fromRefMap,
WeakRemoteReferenceMap<GenSSRemoteReference> toRefMap,
boolean isMinorCollection) {
final boolean isForwarder = objects().hasForwardingAddressUnsafe(fromOrigin);
GenSSRemoteReference ref = null;
ref = fromRefMap.get(fromOrigin);
if (ref != null) {
// A reference to the object is already in the from-space map. Check if it was forwarded and if so, update the ref and maps accordingly.
if (!ref.status().isForwarder() && isForwarder) {
final Address toOrigin = objects().getForwardingAddressUnsafe(fromOrigin);
fromRefMap.remove(fromOrigin);
ref.discoverForwarded(toOrigin, isMinorCollection);
toRefMap.put(toOrigin, ref);
// Create a forwarder and add it to the from-space map.
fromRefMap.put(fromOrigin, GenSSRemoteReference.createForwarder(this, ref));
}
} else {
if (isForwarder) {
/*
* A newly discovered object in the old From-Space that is forwarded.
* Check to see if we already know about the copy in To-Space.
*/
final Address toOrigin = objects().getForwardingAddressUnsafe(fromOrigin);
GenSSRemoteReference forwardedRef = toRefMap.get(toOrigin);
if (forwardedRef != null) {
if (forwardedRef.forwardedFrom().isZero()) {
/*
* We already have a reference to the new copy of the forwarded object in the old To-Space:
* transition state and add it to the From-Space map (indexed by its fromOrigin).
*/
forwardedRef.discoverForwarder(fromOrigin, isMinorCollection);
} else {
// This may occur when the forwarder ref previously stored in the from map was collected by the GC, leaving
// only the weak ref with no referent.
TeleError.check(forwardedRef.forwardedFrom().equals(fromOrigin));
}
ref = GenSSRemoteReference.createForwarder(this, forwardedRef);
fromRefMap.put(fromOrigin, ref);
} else if (objects().isPlausibleOriginUnsafe(toOrigin)) {
/*
* A newly discovered object that is forwarded, but whose new copy in To-Space we
* haven't seen yet; add the reference to both the From-Space map, indexed by
* "forwardedFrom" origin, and to the To-Space map, where it is indexed by its new
* origin.
*/
forwardedRef = GenSSRemoteReference.createFromTo(this, fromOrigin, toOrigin, isMinorCollection);
toRefMap.put(toOrigin, forwardedRef);
// Create a forwarder and add it to the from-space map.
ref = GenSSRemoteReference.createForwarder(this, forwardedRef);
fromRefMap.put(fromOrigin, ref);
}
} else if (objects().isPlausibleOriginUnsafe(fromOrigin)) {
/*
* A newly discovered object in the old From-Space that is not forwarded; add
* a new reference to the old From-Space map, where it is indexed by its origin in From-Space.
*/
ref = GenSSRemoteReference.createFromOnly(this, fromOrigin, isMinorCollection);
fromRefMap.put(fromOrigin, ref);
}
}
return ref;
}
@Override
public void printObjectSessionStats(PrintStream printStream, int indent, boolean verbose) {
// TODO
}
public static class TeleGenSSHeapScheme extends TeleHeapScheme {
private RemoteReference oldSpaceReference = referenceManager().zeroReference();
private RemoteReference resizingPolicyReference = referenceManager().zeroReference();
public TeleGenSSHeapScheme(TeleVM vm, RemoteReference reference) {
super(vm, reference);
}
public TeleCardTableRSet readTeleCardTableRSet() {
final RemoteReference cardTableRSetReference = fields().GenSSHeapScheme_cardTableRSet.readRemoteReference(reference());
if (cardTableRSetReference.isZero()) {
return null;
}
return (TeleCardTableRSet) objects().makeTeleObject(cardTableRSetReference);
}
public TeleContiguousHeapSpace readTeleYoungSpace() {
final RemoteReference youngSpaceReference = fields().GenSSHeapScheme_youngSpace.readRemoteReference(reference());
if (youngSpaceReference.isZero()) {
return null;
}
return (TeleContiguousHeapSpace) objects().makeTeleObject(fields().ContiguousAllocatingSpace_space.readRemoteReference(youngSpaceReference));
}
public TeleContiguousHeapSpace readTeleOldToSpace() {
if (oldSpaceReference.isZero()) {
oldSpaceReference = fields().GenSSHeapScheme_oldSpace.readRemoteReference(reference());
if (oldSpaceReference.isZero()) {
return null;
}
}
return (TeleContiguousHeapSpace) objects().makeTeleObject(fields().ContiguousAllocatingSpace_space.readRemoteReference(oldSpaceReference));
}
public TeleContiguousHeapSpace readTeleOldFromSpace() {
if (oldSpaceReference.isZero()) {
oldSpaceReference = fields().GenSSHeapScheme_oldSpace.readRemoteReference(reference());
if (oldSpaceReference.isZero()) {
return null;
}
}
return (TeleContiguousHeapSpace) objects().makeTeleObject(fields().ContiguousSemiSpace_fromSpace.readRemoteReference(oldSpaceReference));
}
private TeleBaseAtomicBumpPointerAllocator readTeleBumpAllocator(TeleInstanceReferenceFieldAccess spaceFieldAccess) {
RemoteReference spaceReference = spaceFieldAccess.readRemoteReference(reference());
if (spaceReference.isZero()) {
return null;
}
return (TeleBaseAtomicBumpPointerAllocator) objects().makeTeleObject(fields().ContiguousAllocatingSpace_allocator.readRemoteReference(spaceReference));
}
public TeleBaseAtomicBumpPointerAllocator readOldSpaceAllocator() {
return readTeleBumpAllocator(fields().GenSSHeapScheme_oldSpace);
}
public TeleBaseAtomicBumpPointerAllocator readYoungSpaceAllocator() {
return readTeleBumpAllocator(fields().GenSSHeapScheme_youngSpace);
}
public int fullCollectionCount() {
return fields().GenSSHeapScheme_fullCollectionCount.readInt(reference());
}
public boolean requiresFullCollection() {
return fields().GenSSHeapScheme_requiresFullGC.readBoolean(reference());
}
public RemoteReference oldSpaceEvacuator() {
return fields().GenSSHeapScheme_oldSpaceEvacuator.readRemoteReference(reference());
}
public RemoteReference youngSpaceEvacuator() {
return fields().GenSSHeapScheme_youngSpaceEvacuator.readRemoteReference(reference());
}
/**
* Return the initial evacuation mark of evacuator.
* @return an address in the To-space of the old generation
*/
public Address initialEvacuationMark(RemoteReference evacuator) {
return fields().EvacuatorToCardSpace_initialEvacuationMark.readWord(evacuator).asAddress();
}
public Address youngOverflowEvacuationMark() {
return fields().GenSSHeapScheme_youngOverflowEvacuationMark.readWord(reference()).asAddress();
}
public Address oldOverflowEvacuationMark() {
return fields().GenSSHeapScheme_oldOverflowEvacuationMark.readWord(reference()).asAddress();
}
/**
* Return top-most allocated mark within the current evacuation buffer of the evacuator.
* @return an address in the evacuation buffer of an evacuator
*/
public Address ptop(RemoteReference evacuator) {
return fields().EvacuatorToCardSpace_ptop.readWord(evacuator).asAddress();
}
private RemoteReference resizingPolicy() {
if (resizingPolicyReference.isZero()) {
resizingPolicyReference = fields().GenSSHeapScheme_resizingPolicy.readRemoteReference(reference());
}
return resizingPolicyReference;
}
public boolean oldEvacuationOverflow() {
if (resizingPolicy().isZero()) {
return false;
}
return fields().GenSSHeapSizingPolicy_oldEvacuationOverflow.readBoolean(resizingPolicyReference);
}
public boolean minorEvacuationOverflow() {
if (resizingPolicy().isZero()) {
return false;
}
return fields().GenSSHeapSizingPolicy_minorEvacuationOverflow.readBoolean(resizingPolicyReference);
}
public TeleMemoryRegion readYoungOverflowArea() {
return (TeleMemoryRegion) objects().makeTeleObject(fields().GenSSHeapScheme_overflowedArea.readRemoteReference(reference()));
}
}
}