package com.oracle.svm.core.genscavenge;
import static org.graalvm.compiler.nodes.extended.BranchProbabilityNode.SLOW_PATH_PROBABILITY;
import static org.graalvm.compiler.nodes.extended.BranchProbabilityNode.probability;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import org.graalvm.compiler.word.Word;
import org.graalvm.word.Pointer;
import org.graalvm.word.UnsignedWord;
import org.graalvm.word.WordFactory;
import com.oracle.svm.core.annotate.AlwaysInline;
import com.oracle.svm.core.heap.Heap;
import com.oracle.svm.core.heap.ObjectReferenceVisitor;
import com.oracle.svm.core.heap.ReferenceInternals;
import com.oracle.svm.core.hub.DynamicHub;
import com.oracle.svm.core.log.Log;
import com.oracle.svm.core.snippets.KnownIntrinsics;
import com.oracle.svm.core.thread.VMOperation;
import com.oracle.svm.core.util.UnsignedUtils;
final class ReferenceObjectProcessing {
private static Reference<?> rememberedRefsList;
private static UnsignedWord maxSoftRefAccessIntervalMs = UnsignedUtils.MAX_VALUE;
private static boolean softReferencesAreWeak = false;
private static long initialSoftRefClock = 0;
private ReferenceObjectProcessing() {
}
public static void setSoftReferencesAreWeak(boolean enabled) {
assert VMOperation.isGCInProgress();
softReferencesAreWeak = enabled;
}
@AlwaysInline("GC performance")
public static void discoverIfReference(Object object, ObjectReferenceVisitor refVisitor) {
assert object != null;
DynamicHub hub = KnownIntrinsics.readHub(object);
if (probability(SLOW_PATH_PROBABILITY, hub.isReferenceInstanceClass())) {
discover(object, refVisitor);
}
}
private static void discover(Object obj, ObjectReferenceVisitor refVisitor) {
Reference<?> dr = KnownIntrinsics.convertUnknownValue(obj, Reference.class);
Log trace = Log.noopLog().string("[ReferenceObjectProcessing.discover: ").object(dr);
if (ReferenceInternals.getNextDiscovered(dr) != null) {
trace.string(" already discovered]").newline();
return;
}
Pointer referentAddr = ReferenceInternals.getReferentPointer(dr);
trace.string(" referent: ").hex(referentAddr);
if (referentAddr.isNull()) {
trace.string(" is inactive]").newline();
return;
}
if (Heap.getHeap().isInImageHeap(referentAddr)) {
trace.string(" is in image heap]").newline();
return;
}
if (maybeUpdateForwardedReference(dr, referentAddr)) {
trace.string(" has already been promoted and field has been updated]").newline();
return;
}
if (willSurviveThisCollection(referentAddr.toObject())) {
HeapImpl.getHeapImpl().dirtyCardIfNecessary(dr, referentAddr.toObject());
trace.string(" referent is in a to-space]").newline();
return;
}
if (!softReferencesAreWeak && dr instanceof SoftReference) {
long clock = ReferenceInternals.getSoftReferenceClock();
long timestamp = ReferenceInternals.getSoftReferenceTimestamp((SoftReference<?>) dr);
if (timestamp == 0) {
timestamp = initialSoftRefClock;
}
UnsignedWord elapsed = WordFactory.unsigned(clock - timestamp);
if (elapsed.belowThan(maxSoftRefAccessIntervalMs)) {
refVisitor.visitObjectReference(ReferenceInternals.getReferentFieldAddress(dr), true);
return;
}
}
trace.string(" remembered to revisit later]").newline();
Reference<?> next = (rememberedRefsList != null) ? rememberedRefsList : dr;
ReferenceInternals.setNextDiscovered(dr, next);
rememberedRefsList = dr;
}
static Reference<?> processRememberedReferences() {
Reference<?> pendingHead = null;
for (Reference<?> current = popRememberedRef(); current != null; current = popRememberedRef()) {
if (!processRememberedRef(current)) {
if (ReferenceInternals.hasQueue(current)) {
ReferenceInternals.setNextDiscovered(current, pendingHead);
pendingHead = current;
}
HeapImpl.getHeapImpl().dirtyCardIfNecessary(current, pendingHead);
}
}
assert rememberedRefsList == null;
return pendingHead;
}
static void afterCollection(UnsignedWord usedBytes, UnsignedWord maxBytes) {
assert rememberedRefsList == null;
UnsignedWord unusedMbytes = maxBytes.subtract(usedBytes).unsignedDivide(1024 * 1024 );
maxSoftRefAccessIntervalMs = unusedMbytes.multiply(HeapOptions.SoftRefLRUPolicyMSPerMB.getValue());
ReferenceInternals.updateSoftReferenceClock();
if (initialSoftRefClock == 0) {
initialSoftRefClock = ReferenceInternals.getSoftReferenceClock();
}
}
private static boolean processRememberedRef(Reference<?> dr) {
Pointer refPointer = ReferenceInternals.getReferentPointer(dr);
if (refPointer.isNull()) {
return true;
}
assert !HeapImpl.getHeapImpl().isInImageHeap(refPointer) : "Image heap referent: should not have been discovered";
if (maybeUpdateForwardedReference(dr, refPointer)) {
return true;
}
Object refObject = refPointer.toObject();
if (willSurviveThisCollection(refObject)) {
HeapImpl.getHeapImpl().dirtyCardIfNecessary(dr, refObject);
return true;
}
ReferenceInternals.clear(dr);
return false;
}
private static boolean maybeUpdateForwardedReference(Reference<?> dr, Pointer referentAddr) {
UnsignedWord header = ObjectHeaderImpl.readHeaderFromPointer(referentAddr);
if (ObjectHeaderImpl.isForwardedHeader(header)) {
Pointer forwardedPointer = Word.objectToUntrackedPointer(ObjectHeaderImpl.getForwardedObject(referentAddr));
ReferenceInternals.setReferentPointer(dr, forwardedPointer);
HeapImpl.getHeapImpl().dirtyCardIfNecessary(dr, forwardedPointer.toObject());
return true;
}
return false;
}
private static boolean willSurviveThisCollection(Object obj) {
HeapChunk.Header<?> chunk = HeapChunk.getEnclosingHeapChunk(obj);
Space space = HeapChunk.getSpace(chunk);
return !space.isFromSpace();
}
private static Reference<?> popRememberedRef() {
Reference<?> result = rememberedRefsList;
if (result != null) {
Reference<?> next = ReferenceInternals.getNextDiscovered(result);
rememberedRefsList = (next != result) ? next : null;
ReferenceInternals.setNextDiscovered(result, null);
}
return result;
}
public static boolean verify(Reference<?> dr) {
Pointer refPointer = ReferenceInternals.getReferentPointer(dr);
int refClassification = HeapVerifier.classifyPointer(refPointer);
if (refClassification < 0) {
Log witness = Log.log();
witness.string("[ReferenceObjectProcessing.verify:");
witness.string(" epoch: ").unsigned(HeapImpl.getHeapImpl().getGCImpl().getCollectionEpoch());
witness.string(" refClassification: ").signed(refClassification);
witness.string("]").newline();
assert (!(refClassification < 0)) : "Bad referent.";
return false;
}
HeapImpl heap = HeapImpl.getHeapImpl();
YoungGeneration youngGen = heap.getYoungGeneration();
OldGeneration oldGen = heap.getOldGeneration();
boolean refNull = refPointer.isNull();
boolean refBootImage = (!refNull) && heap.isInImageHeapSlow(refPointer);
boolean refYoung = (!refNull) && youngGen.slowlyFindPointer(refPointer);
boolean refOldFrom = (!refNull) && oldGen.slowlyFindPointerInFromSpace(refPointer);
boolean refOldTo = (!refNull) && oldGen.slowlyFindPointerInToSpace(refPointer);
if (!(refNull || refYoung || refBootImage || refOldFrom)) {
Log witness = Log.log();
witness.string("[ReferenceObjectProcessing.verify:");
witness.string(" epoch: ").unsigned(HeapImpl.getHeapImpl().getGCImpl().getCollectionEpoch());
witness.string(" refBootImage: ").bool(refBootImage);
witness.string(" refYoung: ").bool(refYoung);
witness.string(" refOldFrom: ").bool(refOldFrom);
witness.string(" referent should be in heap.");
witness.string("]").newline();
return false;
}
assert !refOldTo : "referent should be in the heap.";
return true;
}
}