mirror of https://github.com/sbt/sbt.git
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:
parent
f4a2bcd0ce
commit
3a6ff8afca
|
|
@ -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) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue