Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@
*/
package com.oracle.svm.core.genscavenge;

import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
import com.oracle.svm.core.feature.InternalFeature;
import com.oracle.svm.core.heap.GCCause;
import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;

final class GenScavengeGCCause extends GCCause {
public static final GCCause OnAllocation = new GenScavengeGCCause("Collect on allocation", 10);
Expand All @@ -33,3 +36,19 @@ private GenScavengeGCCause(String name, int id) {
super(name, id);
}
}

/**
* For layered builds we must eagerly register all GCCauses in the initial layer.
*/
@AutomaticallyRegisteredFeature
class GenScavengeGCCauseRegistration implements InternalFeature {
@Override
public boolean isInConfiguration(IsInConfigurationAccess access) {
return ImageLayerBuildingSupport.buildingInitialLayer();
}

@Override
public void duringSetup(DuringSetupAccess access) {
GCCause.registerGCCause(GenScavengeGCCause.OnAllocation);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@
*/
package com.oracle.svm.core.heap;

import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.function.Function;

import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
Expand All @@ -34,10 +37,19 @@
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton;
import com.oracle.svm.core.feature.InternalFeature;
import com.oracle.svm.core.imagelayer.BuildingImageLayerPredicate;
import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;
import com.oracle.svm.core.layeredimagesingleton.ImageSingletonLoader;
import com.oracle.svm.core.layeredimagesingleton.ImageSingletonWriter;
import com.oracle.svm.core.layeredimagesingleton.InitialLayerOnlyImageSingleton;
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingleton;
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonBuilderFlags;
import com.oracle.svm.core.util.DuplicatedInNativeCode;
import com.oracle.svm.core.util.ImageHeapList;
import com.oracle.svm.core.util.VMError;

import jdk.graal.compiler.debug.Assertions;

/**
* This class holds garbage collection causes that are common and therefore shared between different
* garbage collector implementations.
Expand Down Expand Up @@ -77,34 +89,138 @@ public static GCCause fromId(int causeId) {
public static List<GCCause> getGCCauses() {
return ImageSingletons.lookup(GCCauseSupport.class).gcCauses;
}

@Platforms(Platform.HOSTED_ONLY.class)
public static void registerGCCause(GCCause cause) {
ImageSingletons.lookup(GCCauseSupport.class).installGCCause(cause);
}
}

@AutomaticallyRegisteredImageSingleton
class GCCauseSupport {
class GCCauseSupport implements InitialLayerOnlyImageSingleton {
final List<GCCause> gcCauses = ImageHeapList.create(GCCause.class, null);

@Platforms(Platform.HOSTED_ONLY.class)
Object collectGCCauses(Object obj) {
if (obj instanceof GCCause gcCause) {
synchronized (gcCauses) {
int id = gcCause.getId();
while (gcCauses.size() <= id) {
gcCauses.add(null);
}
var existing = gcCauses.set(id, gcCause);
if (existing != null && existing != gcCause) {
throw VMError.shouldNotReachHere("Two GCCause objects have the same id " + id + ": " + gcCause.getName() + ", " + existing.getName());
}
installGCCause(gcCause);
}
}
return obj;
}

@Platforms(Platform.HOSTED_ONLY.class)
void installGCCause(GCCause gcCause) {
int id = gcCause.getId();
while (gcCauses.size() <= id) {
gcCauses.add(null);
}
var existing = gcCauses.set(id, gcCause);
if (existing != null && existing != gcCause) {
throw VMError.shouldNotReachHere("Two GCCause objects have the same id " + id + ": " + gcCause.getName() + ", " + existing.getName());
}
}

@Override
public boolean accessibleInFutureLayers() {
return true;
}

@Override
public EnumSet<LayeredImageSingletonBuilderFlags> getImageBuilderFlags() {
return LayeredImageSingletonBuilderFlags.ALL_ACCESS;
}
}

@AutomaticallyRegisteredFeature
class GCCauseFeature implements InternalFeature {

@Override
public void duringSetup(DuringSetupAccess access) {
access.registerObjectReplacer(ImageSingletons.lookup(GCCauseSupport.class)::collectGCCauses);
if (!ImageLayerBuildingSupport.buildingImageLayer()) {
/*
* In traditional builds we lazily register GCCauses as they become visible to the
* native-image generator.
*/
access.registerObjectReplacer(ImageSingletons.lookup(GCCauseSupport.class)::collectGCCauses);
} else {
/*
* For layered builds we eagerly register all GCCauses in the initial layer. In all
* layers, via an object replacer, we then validate all referenced GCCauses have been
* registered.
*/
Function<Integer, String> idToGCCauseName;
if (ImageLayerBuildingSupport.buildingInitialLayer()) {
GCCauseSupport support = ImageSingletons.lookup(GCCauseSupport.class);
support.installGCCause(GCCause.JavaLangSystemGC);
support.installGCCause(GCCause.UnitTest);
support.installGCCause(GCCause.TestGCInDeoptimizer);
support.installGCCause(GCCause.HintedGC);
support.installGCCause(GCCause.JvmtiForceGC);
support.installGCCause(GCCause.HeapDump);
support.installGCCause(GCCause.DiagnosticCommand);

var gcCauseList = GCCause.getGCCauses();
idToGCCauseName = (idx) -> gcCauseList.get(idx).getName();
} else {
var gcCauseNames = LayeredGCCauseTracker.getRegisteredGCCauses();
idToGCCauseName = gcCauseNames::get;
}
access.registerObjectReplacer(obj -> {
if (obj instanceof GCCause gcCause) {
if (!idToGCCauseName.apply(gcCause.getId()).equals(gcCause.getName())) {
var id = gcCause.getId();
VMError.shouldNotReachHere("Mismatch in GCCause name for id %s: %s %s", id, idToGCCauseName.apply(id), gcCause.getName());
}
}
return obj;
});
}
}
}

/**
* In layered builds all {@link GCCause}s are registered and installed in the initial layer. Here we
* track which {@link GCCause}s were installed in the initial layer to detect issues.
*/
@AutomaticallyRegisteredImageSingleton(onlyWith = BuildingImageLayerPredicate.class)
class LayeredGCCauseTracker implements LayeredImageSingleton {
List<String> registeredGCCauses;

public static List<String> getRegisteredGCCauses() {
assert ImageLayerBuildingSupport.buildingExtensionLayer();
return ImageSingletons.lookup(LayeredGCCauseTracker.class).registeredGCCauses;
}

@Override
public EnumSet<LayeredImageSingletonBuilderFlags> getImageBuilderFlags() {
return LayeredImageSingletonBuilderFlags.BUILDTIME_ACCESS_ONLY;
}

@Override
public PersistFlags preparePersist(ImageSingletonWriter writer) {
List<String> gcCauses;
if (ImageLayerBuildingSupport.buildingInitialLayer()) {
gcCauses = GCCause.getGCCauses().stream().map(gcCause -> {
if (gcCause == null) {
return "";
} else {
assert !gcCause.getName().isEmpty() : Assertions.errorMessage("Empty string is reserved for non-existent GCCauses", gcCause);
return gcCause.getName();
}
}).toList();
} else {
gcCauses = registeredGCCauses;
}
writer.writeStringList("registeredGCCauses", gcCauses);
return PersistFlags.CREATE;
}

@SuppressWarnings("unused")
public static Object createFromLoader(ImageSingletonLoader loader) {
var causeTracker = new LayeredGCCauseTracker();
causeTracker.registeredGCCauses = Collections.unmodifiableList(loader.readStringList("registeredGCCauses"));
return causeTracker;
}
}