Use global classloader cache for scala instance

I noticed in a heap dump of sbt that there were many classloaders for
the scala instance. I then realized that we were making a new
classloader for the scala library on every test run. Even worse, the
ScalaInstanceLoader instance was never closed which lead to a metaspace
leak. I moved the scala instance classloader to the global classloader
cache. Not only will these be correctly cached, they will be closed if
evicted from the cache.
This commit is contained in:
Ethan Atkins 2019-04-30 12:01:48 -07:00
parent f4a2bcd0ce
commit 3a6ff8afca
1 changed files with 6 additions and 12 deletions

View File

@ -40,6 +40,7 @@ private[sbt] object ClassLoaders {
rawRuntimeDependencies =
dependencyJars(Runtime / dependencyClasspath).value.filterNot(exclude),
allDependencies = dependencyJars(dependencyClasspath).value.filterNot(exclude),
globalCache = (Scope.GlobalScope / classLoaderCache).value,
runtimeCache = (Runtime / classLoaderCache).value,
testCache = (Test / classLoaderCache).value,
resources = ClasspathUtilities.createClasspathResources(fullCP, si),
@ -73,6 +74,7 @@ private[sbt] object ClassLoaders {
)
s.log.warn(s"$showJavaOptions will be ignored, $showFork is set to false")
}
val globalCache = (Scope.GlobalScope / classLoaderCache).value
val runtimeCache = (Runtime / classLoaderCache).value
val testCache = (Test / classLoaderCache).value
val exclude = dependencyJars(exportedProducts).value.toSet ++ instance.allJars
@ -86,6 +88,7 @@ private[sbt] object ClassLoaders {
fullCP = classpath,
rawRuntimeDependencies = runtimeDeps,
allDependencies = allDeps,
globalCache = globalCache,
runtimeCache = runtimeCache,
testCache = testCache,
resources = ClasspathUtilities.createClasspathResources(classpath, instance),
@ -114,6 +117,7 @@ private[sbt] object ClassLoaders {
fullCP: Seq[File],
rawRuntimeDependencies: Seq[File],
allDependencies: Seq[File],
globalCache: ClassLoaderCache,
runtimeCache: ClassLoaderCache,
testCache: ClassLoaderCache,
resources: Map[String, String],
@ -143,7 +147,8 @@ private[sbt] object ClassLoaders {
val allTestDependencies = if (layerTestDependencies) allDependenciesSet else Set.empty[File]
val allRuntimeDependencies = (if (layerDependencies) rawRuntimeDependencies else Nil).toSet
val scalaInstanceLayer = new ScalaInstanceLoader(si)
val scalaInstanceLayer =
globalCache.get((si.allJars.toSeq, interfaceLoader, resources, tmp))
// layer 2
val runtimeDependencySet = allDependenciesSet intersect allRuntimeDependencies
val runtimeDependencies = rawRuntimeDependencies.filter(runtimeDependencySet)
@ -187,17 +192,6 @@ private[sbt] object ClassLoaders {
if (snapshots.isEmpty) jarLoader else cache.get((snapshots, jarLoader, resources, tmp))
}
private class ScalaInstanceLoader(val instance: ScalaInstance)
extends URLClassLoader(instance.allJars.map(_.toURI.toURL), interfaceLoader) {
override def equals(o: Any): Boolean = o match {
case that: ScalaInstanceLoader => this.instance.allJars.sameElements(that.instance.allJars)
case _ => false
}
override def hashCode: Int = instance.hashCode
override lazy val toString: String =
s"ScalaInstanceLoader($interfaceLoader, jars = {${instance.allJars.mkString(", ")}})"
}
// helper methods
private def flatLoader(classpath: Seq[File], parent: ClassLoader): ClassLoader =
new URLClassLoader(classpath.map(_.toURI.toURL).toArray, parent) {