Fix byte array memory leak from MemoryBasedClassLoader
40 Mb of bytes of preloaded compiler classes during the compilation become 16 Mb after this change
This commit is contained in:
@@ -17,6 +17,8 @@
|
||||
package org.jetbrains.kotlin.preloading;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.*;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.JarFile;
|
||||
@@ -29,8 +31,6 @@ public class ClassPreloadingUtils {
|
||||
/**
|
||||
* Creates a class loader that loads all classes from {@code jarFiles} into memory to make loading faster (avoid skipping through zip archives).
|
||||
*
|
||||
* NOTE: if many resources with the same name exist, only the first one will be loaded
|
||||
*
|
||||
* @param jarFiles jars to load all classes from
|
||||
* @param classCountEstimation an estimated number of classes in a the jars
|
||||
* @param parentClassLoader parent class loader
|
||||
@@ -53,7 +53,15 @@ public class ClassPreloadingUtils {
|
||||
parentClassLoader = preloadClasses(classpath, classCountEstimation, parentClassLoader, null, handler);
|
||||
}
|
||||
|
||||
return new MemoryBasedClassLoader(classesToLoadByParent, parentClassLoader, entries, handler);
|
||||
return new MemoryBasedClassLoader(classesToLoadByParent, createFallbackClassLoader(jarFiles, parentClassLoader), entries, handler);
|
||||
}
|
||||
|
||||
private static URLClassLoader createFallbackClassLoader(Collection<File> files, ClassLoader parent) throws IOException {
|
||||
List<URL> urls = new ArrayList<URL>(files.size());
|
||||
for (File file : files) {
|
||||
urls.add(file.toURI().toURL());
|
||||
}
|
||||
return new URLClassLoader(urls.toArray(new URL[urls.size()]), parent);
|
||||
}
|
||||
|
||||
public static ClassLoader preloadClasses(
|
||||
|
||||
@@ -21,6 +21,14 @@ import java.net.URL;
|
||||
import java.util.*;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
/**
|
||||
* A class loader which loads classes and resources from the given map.
|
||||
*
|
||||
* To save memory, as soon as any class is loaded, its bytecode is removed from the map.
|
||||
* This means that once any class is loaded, it _cannot be found_ as a resource anymore.
|
||||
* Therefore if you need to be able to find classes via findResource(), you should pass a parent
|
||||
* class loader which is able to do that at any point of time.
|
||||
*/
|
||||
public class MemoryBasedClassLoader extends ClassLoader {
|
||||
private final ClassCondition classesToLoadByParent;
|
||||
private final ClassLoader parent;
|
||||
@@ -77,6 +85,9 @@ public class MemoryBasedClassLoader extends ClassLoader {
|
||||
Object resources = preloadedResources.get(internalName);
|
||||
if (resources == null) return null;
|
||||
|
||||
// Clear the resource, we won't need it anymore
|
||||
preloadedResources.remove(internalName);
|
||||
|
||||
ResourceData resourceData = resources instanceof ResourceData
|
||||
? ((ResourceData) resources)
|
||||
: ((List<ResourceData>) resources).get(0);
|
||||
|
||||
@@ -135,7 +135,11 @@ public class PathUtil {
|
||||
|
||||
@NotNull
|
||||
public static File getResourcePathForClass(@NotNull Class aClass) {
|
||||
String resourceRoot = PathManager.getResourceRoot(aClass, "/" + aClass.getName().replace('.', '/') + ".class");
|
||||
String path = "/" + aClass.getName().replace('.', '/') + ".class";
|
||||
String resourceRoot = PathManager.getResourceRoot(aClass, path);
|
||||
if (resourceRoot == null) {
|
||||
throw new IllegalStateException("Resource not found: " + path);
|
||||
}
|
||||
return new File(resourceRoot).getAbsoluteFile();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user