diff --git a/main/src/main/scala/sbt/Cross.scala b/main/src/main/scala/sbt/Cross.scala index c023ec9a3..0a69895a8 100644 --- a/main/src/main/scala/sbt/Cross.scala +++ b/main/src/main/scala/sbt/Cross.scala @@ -69,6 +69,14 @@ object Cross { ) (settings, excludeKeys(Set(scalaVersion.key, scalaHome.key))) } + + val isForceGc = getOpt(Keys.forcegc in Global) getOrElse GCUtil.defaultForceGarbageCollection + // This is how to get the interval, but ignore it, and just forcegc + // val gcInterval = getOpt(Keys.minForcegcInterval in Global) getOrElse GCUtil.defaultMinForcegcInterval + if (isForceGc) { + GCUtil.forceGc(state.log) + } + // TODO - Track delegates and avoid regenerating. val delegates: Seq[Setting[_]] = session.mergeSettings collect { case x if exclude(x) => delegateToGlobal(x.key) diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 7d1145b21..b50ddefda 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -156,8 +156,8 @@ object Defaults extends BuildCommon { maxErrors :== 100, fork :== false, initialize :== {}, - forcegc :== sys.props.get("sbt.task.forcegc").map(java.lang.Boolean.parseBoolean).getOrElse(EvaluateTaskConfig.defaultForceGarbageCollection), - minForcegcInterval :== sys.props.get("sbt.task.minForcegcInterval").map(java.lang.Integer.parseInt).getOrElse(EvaluateTaskConfig.defaultMinForcegcInterval) + forcegc :== sys.props.get("sbt.task.forcegc").map(java.lang.Boolean.parseBoolean).getOrElse(GCUtil.defaultForceGarbageCollection), + minForcegcInterval :== sys.props.get("sbt.task.minForcegcInterval").map(java.lang.Integer.parseInt).getOrElse(GCUtil.defaultMinForcegcInterval) )) def defaultTestTasks(key: Scoped): Seq[Setting[_]] = inTask(key)(Seq( tags := Seq(Tags.Test -> 1), diff --git a/main/src/main/scala/sbt/EvaluateTask.scala b/main/src/main/scala/sbt/EvaluateTask.scala index ba0e9bd03..6e57f9326 100644 --- a/main/src/main/scala/sbt/EvaluateTask.scala +++ b/main/src/main/scala/sbt/EvaluateTask.scala @@ -93,11 +93,6 @@ sealed trait EvaluateTaskConfig { def minForcegcInterval: Int } final object EvaluateTaskConfig { - // Returns the default force garbage collection flag, - // as specified by system properties. - private[sbt] def defaultForceGarbageCollection: Boolean = true - private[sbt] def defaultMinForcegcInterval: Int = 60 - /** Pulls in the old configuration format. */ def apply(old: EvaluateConfig): EvaluateTaskConfig = { object AdaptedTaskConfig extends EvaluateTaskConfig { @@ -107,8 +102,8 @@ final object EvaluateTaskConfig { def cancelStrategy: TaskCancellationStrategy = if (old.cancelable) TaskCancellationStrategy.Signal else TaskCancellationStrategy.Null - def forceGarbageCollection = defaultForceGarbageCollection - def minForcegcInterval = defaultMinForcegcInterval + def forceGarbageCollection = GCUtil.defaultForceGarbageCollection + def minForcegcInterval = GCUtil.defaultMinForcegcInterval } AdaptedTaskConfig } @@ -120,7 +115,7 @@ final object EvaluateTaskConfig { cancelStrategy: TaskCancellationStrategy, forceGarbageCollection: Boolean): EvaluateTaskConfig = apply(restrictions, checkCycles, progressReporter, cancelStrategy, forceGarbageCollection, - defaultMinForcegcInterval) + GCUtil.defaultMinForcegcInterval) /** Raw constructor for EvaluateTaskConfig. */ def apply(restrictions: Seq[Tags.Rule], @@ -233,10 +228,10 @@ object EvaluateTask { } // TODO - Should this pull from Global or from the project itself? private[sbt] def forcegc(extracted: Extracted, structure: BuildStructure): Boolean = - getSetting(Keys.forcegc in Global, EvaluateTaskConfig.defaultForceGarbageCollection, extracted, structure) + getSetting(Keys.forcegc in Global, GCUtil.defaultForceGarbageCollection, extracted, structure) // TODO - Should this pull from Global or from the project itself? private[sbt] def minForcegcInterval(extracted: Extracted, structure: BuildStructure): Int = - getSetting(Keys.minForcegcInterval in Global, EvaluateTaskConfig.defaultMinForcegcInterval, extracted, structure) + getSetting(Keys.minForcegcInterval in Global, GCUtil.defaultMinForcegcInterval, extracted, structure) def getSetting[T](key: SettingKey[T], default: T, extracted: Extracted, structure: BuildStructure): T = key in extracted.currentRef get structure.data getOrElse default diff --git a/main/src/main/scala/sbt/GCUtil.scala b/main/src/main/scala/sbt/GCUtil.scala index 5f0bfc4e7..5dbd22a5e 100644 --- a/main/src/main/scala/sbt/GCUtil.scala +++ b/main/src/main/scala/sbt/GCUtil.scala @@ -4,6 +4,10 @@ import java.util.concurrent.atomic.AtomicLong import scala.util.control.NonFatal private[sbt] object GCUtil { + // Returns the default force garbage collection flag, + // as specified by system properties. + val defaultForceGarbageCollection: Boolean = true + val defaultMinForcegcInterval: Int = 60 val lastGcCheck: AtomicLong = new AtomicLong(0L) def forceGcWithInterval(minForcegcInterval: Int, log: Logger): Unit = diff --git a/notes/0.13.8/force-gc.markdown b/notes/0.13.8/force-gc.markdown deleted file mode 100644 index 5974979a0..000000000 --- a/notes/0.13.8/force-gc.markdown +++ /dev/null @@ -1,12 +0,0 @@ - [@cunei]: https://github.com/cunei - [@eed3si9n]: https://github.com/eed3si9n - [@gkossakowski]: https://github.com/gkossakowski - [@jsuereth]: https://github.com/jsuereth - -### Fixes with compatibility implications - -### Improvements - -### Bug fixes - -- Enables forced GC by default when object pending finalization exceeds some threshold. [@eed3si9n][@eed3si9n] diff --git a/notes/0.13.9/force-gc.markdown b/notes/0.13.9/force-gc.markdown new file mode 100644 index 000000000..f555aa466 --- /dev/null +++ b/notes/0.13.9/force-gc.markdown @@ -0,0 +1,26 @@ + [@cunei]: https://github.com/cunei + [@eed3si9n]: https://github.com/eed3si9n + [@gkossakowski]: https://github.com/gkossakowski + [@jsuereth]: https://github.com/jsuereth + + [1223]: https://github.com/sbt/sbt/issues/1223 + [1773]: https://github.com/sbt/sbt/pull/1773 + +### Fixes with compatibility implications + +### Improvements + +### Bug fixes + +- Enables forced GC by default. See below. + +### Force GC + +[@cunei][@cunei] in [#1223][1223] discovered that sbt leaks PermGen +when it creates classloaders to call Scala Compilers. +sbt 0.13.9 will call GC on a set interval (default: 60s). +It will also call GC right before cross building. +This behavior can diabled using by setting false to `forcegc` +setting or `sbt.task.forcegc` flag. + +[#1773][1773] by [@eed3si9n][@eed3si9n]