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;
|
package org.jetbrains.kotlin.preloading;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.jar.Attributes;
|
import java.util.jar.Attributes;
|
||||||
import java.util.jar.JarFile;
|
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).
|
* 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 jarFiles jars to load all classes from
|
||||||
* @param classCountEstimation an estimated number of classes in a the jars
|
* @param classCountEstimation an estimated number of classes in a the jars
|
||||||
* @param parentClassLoader parent class loader
|
* @param parentClassLoader parent class loader
|
||||||
@@ -53,7 +53,15 @@ public class ClassPreloadingUtils {
|
|||||||
parentClassLoader = preloadClasses(classpath, classCountEstimation, parentClassLoader, null, handler);
|
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(
|
public static ClassLoader preloadClasses(
|
||||||
|
|||||||
@@ -21,6 +21,14 @@ import java.net.URL;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@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 {
|
public class MemoryBasedClassLoader extends ClassLoader {
|
||||||
private final ClassCondition classesToLoadByParent;
|
private final ClassCondition classesToLoadByParent;
|
||||||
private final ClassLoader parent;
|
private final ClassLoader parent;
|
||||||
@@ -77,6 +85,9 @@ public class MemoryBasedClassLoader extends ClassLoader {
|
|||||||
Object resources = preloadedResources.get(internalName);
|
Object resources = preloadedResources.get(internalName);
|
||||||
if (resources == null) return null;
|
if (resources == null) return null;
|
||||||
|
|
||||||
|
// Clear the resource, we won't need it anymore
|
||||||
|
preloadedResources.remove(internalName);
|
||||||
|
|
||||||
ResourceData resourceData = resources instanceof ResourceData
|
ResourceData resourceData = resources instanceof ResourceData
|
||||||
? ((ResourceData) resources)
|
? ((ResourceData) resources)
|
||||||
: ((List<ResourceData>) resources).get(0);
|
: ((List<ResourceData>) resources).get(0);
|
||||||
|
|||||||
@@ -135,7 +135,11 @@ public class PathUtil {
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static File getResourcePathForClass(@NotNull Class aClass) {
|
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();
|
return new File(resourceRoot).getAbsoluteFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user