Skip to content

Commit

Permalink
Merge pull request #42525 from gsmet/various-memory-improvements-expe…
Browse files Browse the repository at this point in the history
…riment2

Create a lossy index of resource -> ClassPathElement mapping
  • Loading branch information
gsmet authored Aug 30, 2024
2 parents 844a541 + dfa1455 commit f7e084b
Show file tree
Hide file tree
Showing 29 changed files with 638 additions and 283 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ public static <T> T readConfig(ApplicationModel appModel, LaunchMode launchMode,
final QuarkusClassLoader.Builder configClBuilder = QuarkusClassLoader.builder("CodeGenerator Config ClassLoader",
deploymentClassLoader, false);
if (!allowedConfigServices.isEmpty()) {
configClBuilder.addElement(new MemoryClassPathElement(allowedConfigServices, true));
configClBuilder.addNormalPriorityElement(new MemoryClassPathElement(allowedConfigServices, true));
}
if (!bannedConfigServices.isEmpty()) {
configClBuilder.addBannedElement(new MemoryClassPathElement(bannedConfigServices, true));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
package io.quarkus.deployment;

import static io.quarkus.commons.classloading.ClassLoaderHelper.fromClassNameToResourceName;

import java.io.StringWriter;
import java.io.Writer;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;

import io.quarkus.bootstrap.BootstrapDebug;
import io.quarkus.bootstrap.classloading.ClassPathElement;
import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.builditem.GeneratedClassBuildItem;
Expand Down Expand Up @@ -75,12 +71,7 @@ public Writer getSourceWriter(String className) {
}

public static boolean isApplicationClass(String className) {
QuarkusClassLoader cl = (QuarkusClassLoader) Thread.currentThread()
.getContextClassLoader();
//if the class file is present in this (and not the parent) CL then it is an application class
List<ClassPathElement> res = cl
.getElementsWithResource(fromClassNameToResourceName(className), true);
return !res.isEmpty();
return QuarkusClassLoader.isApplicationClass(className);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,12 @@
import io.quarkus.bootstrap.app.CuratedApplication;
import io.quarkus.bootstrap.app.RunningQuarkusApplication;
import io.quarkus.bootstrap.app.StartupAction;
import io.quarkus.bootstrap.classloading.ClassPathElement;
import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.bootstrap.logging.InitialConfigurator;
import io.quarkus.bootstrap.runner.Timing;
import io.quarkus.builder.BuildChainBuilder;
import io.quarkus.builder.BuildContext;
import io.quarkus.builder.BuildStep;
import io.quarkus.commons.classloading.ClassLoaderHelper;
import io.quarkus.deployment.builditem.ApplicationClassPredicateBuildItem;
import io.quarkus.deployment.console.ConsoleCommand;
import io.quarkus.deployment.console.ConsoleStateManager;
Expand Down Expand Up @@ -478,14 +476,8 @@ public void execute(BuildContext context) {
//we need to make sure all hot reloadable classes are application classes
context.produce(new ApplicationClassPredicateBuildItem(new Predicate<String>() {
@Override
public boolean test(String s) {
QuarkusClassLoader cl = (QuarkusClassLoader) Thread.currentThread()
.getContextClassLoader();
String resourceName = ClassLoaderHelper.fromClassNameToResourceName(s);
//if the class file is present in this (and not the parent) CL then it is an application class
List<ClassPathElement> res = cl
.getElementsWithResource(resourceName, true);
return !res.isEmpty();
public boolean test(String className) {
return QuarkusClassLoader.isApplicationClass(className);
}
}));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -652,7 +652,7 @@ public String apply(Class<?> aClass) {
//this is a lot more complex
//we need to transform the classes to make the tracing magic work
QuarkusClassLoader deploymentClassLoader = (QuarkusClassLoader) Thread.currentThread().getContextClassLoader();
Set<String> classesToTransform = new HashSet<>(deploymentClassLoader.getLocalClassNames());
Set<String> classesToTransform = new HashSet<>(deploymentClassLoader.getReloadableClassNames());
Map<String, byte[]> transformedClasses = new HashMap<>();
for (String i : classesToTransform) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ public void init() {
+ curatedApplication.getClassLoaderNameSuffix(),
getClass().getClassLoader().getParent(), false);
}
clBuilder.addElement(ClassPathElement.fromDependency(d));
clBuilder.addNormalPriorityElement(ClassPathElement.fromDependency(d));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

import io.quarkus.bootstrap.classloading.ClassPathElement;
import io.quarkus.bootstrap.classloading.QuarkusClassLoader;
import io.quarkus.deployment.IsDevelopment;
import io.quarkus.deployment.IsNormal;
Expand Down Expand Up @@ -155,13 +154,8 @@ public ServiceStartBuildItem searchForTags(CombinedIndexBuildItem combinedIndexB
return null;
}

public boolean isAppClass(String theClassName) {
QuarkusClassLoader cl = (QuarkusClassLoader) Thread.currentThread()
.getContextClassLoader();
//if the class file is present in this (and not the parent) CL then it is an application class
List<ClassPathElement> res = cl
.getElementsWithResource(theClassName.replace(".", "/") + ".class", true);
return !res.isEmpty();
public boolean isAppClass(String className) {
return QuarkusClassLoader.isApplicationClass(className);
}

public static class TracingClassVisitor extends ClassVisitor {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ static ArchivePathTree forPath(Path path, PathFilter filter, boolean manifestEna
this.pathFilter = pathFilter;
}

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

@Override
public Collection<Path> getRoots() {
return List.of(archive);
Expand Down Expand Up @@ -240,6 +245,11 @@ protected OpenArchivePathTree(FileSystem fs) {
this.rootPath = fs.getPath("/");
}

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

@Override
protected Path getContainerPath() {
return ArchivePathTree.this.archive;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ protected DirectoryPathTree(Path dir, PathFilter pathFilter, PathTreeWithManifes
this.dir = dir;
}

@Override
public boolean isArchiveOrigin() {
return false;
}

@Override
protected Path getRootPath() {
return dir;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ public static EmptyPathTree getInstance() {
return INSTANCE;
}

@Override
public boolean isArchiveOrigin() {
return false;
}

@Override
public Collection<Path> getRoots() {
return Collections.emptyList();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ class FilePathTree implements OpenPathTree {
this.pathFilter = pathFilter;
}

@Override
public boolean isArchiveOrigin() {
return false;
}

@Override
public Collection<Path> getRoots() {
return Collections.singletonList(file);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ public FilteredPathTree(PathTree tree, PathFilter filter) {
this.filter = Objects.requireNonNull(filter, "filter is null");
}

@Override
public boolean isArchiveOrigin() {
return original.isArchiveOrigin();
}

@Override
public Collection<Path> getRoots() {
return original.getRoots();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,20 @@ public MultiRootPathTree(PathTree... trees) {
roots = tmp;
}

/**
* If at least one of the PathTrees is not an archive, we return false.
*/
@Override
public boolean isArchiveOrigin() {
for (PathTree tree : trees) {
if (!tree.isArchiveOrigin()) {
return false;
}
}

return true;
}

@Override
public Collection<Path> getRoots() {
return roots;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,13 @@ static PathTree ofArchive(Path archive, PathFilter filter) {
return ArchivePathTree.forPath(archive, filter);
}

/**
* Whether the content of this tree comes from an archive or not.
* <p>
* This is useful for instance when you want to determine if the resources can be updated in dev mode.
*/
boolean isArchiveOrigin();

/**
* The roots of the path tree.
* <p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,11 @@ private CallerOpenPathTree(SharedOpenArchivePathTree delegate) {
this.delegate = delegate;
}

@Override
public boolean isArchiveOrigin() {
return delegate.isArchiveOrigin();
}

@Override
public PathTree getOriginalTree() {
return delegate.getOriginalTree();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ public final class ClassLoaderHelper {
private static final String JDK_INTERNAL = "jdk.internal.";
private static final String SUN_MISC = "sun.misc.";

private static final String CLASS_SUFFIX = ".class";

private ClassLoaderHelper() {
//Not meant to be instantiated
}
Expand All @@ -19,7 +21,23 @@ private ClassLoaderHelper() {
*/
public static String fromClassNameToResourceName(final String className) {
//Important: avoid indy!
return className.replace('.', '/').concat(".class");
return className.replace('.', '/').concat(CLASS_SUFFIX);
}

/**
* Helper method to convert a resource name into the corresponding class name:
* replace all "/" with "." and remove the ".class" postfix.
*
* @param resourceName
* @return the name of the respective class
*/
public static String fromResourceNameToClassName(final String resourceName) {
if (!resourceName.endsWith(CLASS_SUFFIX)) {
throw new IllegalArgumentException(
String.format("%s is not a valid resource name as it doesn't end with .class", resourceName));
}

return resourceName.substring(0, resourceName.length() - CLASS_SUFFIX.length()).replace('/', '.');
}

public static boolean isInJdkPackage(String name) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,10 +183,12 @@ private void addCpElement(QuarkusClassLoader.Builder builder, ResolvedDependency
//we always load this from the parent if it is available, as this acts as a bridge between the running
//app and the dev mode code
builder.addParentFirstElement(element);
builder.addNormalPriorityElement(element);
} else if (dep.isFlagSet(DependencyFlags.CLASSLOADER_LESSER_PRIORITY)) {
builder.addLesserPriorityElement(element);
} else {
builder.addNormalPriorityElement(element);
}
builder.addElement(element);
}

public synchronized QuarkusClassLoader getOrCreateAugmentClassLoader() {
Expand All @@ -212,7 +214,7 @@ public synchronized QuarkusClassLoader getOrCreateAugmentClassLoader() {
}

for (Path i : quarkusBootstrap.getAdditionalDeploymentArchives()) {
builder.addElement(ClassPathElement.fromPath(i, false));
builder.addNormalPriorityElement(ClassPathElement.fromPath(i, false));
}
Map<String, byte[]> banned = new HashMap<>();
for (Collection<String> i : configuredClassLoading.getRemovedResources().values()) {
Expand Down Expand Up @@ -256,7 +258,7 @@ public synchronized QuarkusClassLoader getOrCreateBaseRuntimeClassLoader() {
//there is no need to restart so there is no need for an additional CL

for (Path root : quarkusBootstrap.getApplicationRoot()) {
builder.addElement(ClassPathElement.fromPath(root, true));
builder.addNormalPriorityElement(ClassPathElement.fromPath(root, true));
}
} else {
for (Path root : quarkusBootstrap.getApplicationRoot()) {
Expand All @@ -269,7 +271,7 @@ public synchronized QuarkusClassLoader getOrCreateBaseRuntimeClassLoader() {
for (AdditionalDependency i : quarkusBootstrap.getAdditionalApplicationArchives()) {
if (!i.isHotReloadable()) {
for (Path root : i.getResolvedPaths()) {
builder.addElement(ClassPathElement.fromPath(root, true));
builder.addNormalPriorityElement(ClassPathElement.fromPath(root, true));
}
} else {
for (Path root : i.getResolvedPaths()) {
Expand Down Expand Up @@ -339,15 +341,15 @@ public QuarkusClassLoader createDeploymentClassLoader() {
.setAggregateParentResources(true);

for (Path root : quarkusBootstrap.getApplicationRoot()) {
builder.addElement(ClassPathElement.fromPath(root, true));
builder.addNormalPriorityElement(ClassPathElement.fromPath(root, true));
}

builder.setResettableElement(new MemoryClassPathElement(Collections.emptyMap(), false));

//additional user class path elements first
for (AdditionalDependency i : quarkusBootstrap.getAdditionalApplicationArchives()) {
for (Path root : i.getResolvedPaths()) {
builder.addElement(ClassPathElement.fromPath(root, true));
builder.addNormalPriorityElement(ClassPathElement.fromPath(root, true));
}
}
for (ResolvedDependency dependency : appModel.getDependencies()) {
Expand All @@ -361,7 +363,7 @@ public QuarkusClassLoader createDeploymentClassLoader() {
}
}
for (Path root : configuredClassLoading.getAdditionalClasspathElements()) {
builder.addElement(ClassPathElement.fromPath(root, true));
builder.addNormalPriorityElement(ClassPathElement.fromPath(root, true));
}
return builder.build();
}
Expand All @@ -387,15 +389,15 @@ public QuarkusClassLoader createRuntimeClassLoader(ClassLoader base, Map<String,
.setAggregateParentResources(true);
builder.setTransformedClasses(transformedClasses);

builder.addElement(new MemoryClassPathElement(resources, true));
builder.addNormalPriorityElement(new MemoryClassPathElement(resources, true));
for (Path root : quarkusBootstrap.getApplicationRoot()) {
builder.addElement(ClassPathElement.fromPath(root, true));
builder.addNormalPriorityElement(ClassPathElement.fromPath(root, true));
}

for (AdditionalDependency i : getQuarkusBootstrap().getAdditionalApplicationArchives()) {
if (i.isHotReloadable()) {
for (Path root : i.getResolvedPaths()) {
builder.addElement(ClassPathElement.fromPath(root, true));
builder.addNormalPriorityElement(ClassPathElement.fromPath(root, true));
}
}
}
Expand All @@ -410,7 +412,7 @@ public QuarkusClassLoader createRuntimeClassLoader(ClassLoader base, Map<String,
}
}
for (Path root : configuredClassLoading.getAdditionalClasspathElements()) {
builder.addElement(ClassPathElement.fromPath(root, true));
builder.addNormalPriorityElement(ClassPathElement.fromPath(root, true));
}
return builder.build();
}
Expand Down Expand Up @@ -493,6 +495,11 @@ public Set<String> getProvidedResources() {
return delegate.getProvidedResources().stream().filter(s -> s.endsWith(".class")).collect(Collectors.toSet());
}

@Override
public boolean containsReloadableResources() {
return delegate.containsReloadableResources();
}

@Override
public ProtectionDomain getProtectionDomain() {
return delegate.getProtectionDomain();
Expand Down
Loading

0 comments on commit f7e084b

Please sign in to comment.