Skip to content

Commit 00e11ff

Browse files
committed
SI-8129 Plug a leak in perRunCaches
I guess we've never leaned to heavily on these, because the registry of caches was forgetting about most of them immediately if they compared == at the point of creation. Consequently, maps like `treatedInfos` in Mixin were holding onto old types and symbols and leaking memory. You can reproduce the leak with: (for i in {1..200}; do echo src/library/scala/collection/*.scala; done; printf "\n") | scalac-hash v2.11.0-M7 -J-Xmx256M -Xresident nsc> warning: there were 6 deprecation warning(s); re-run with -deprecation for details (... 15 times) nsc> java.lang.OutOfMemoryError: GC overhead limit exceeded Dumping heap to java_pid90362.hprof ... Heap dump file created [340635728 bytes in 7.993 secs] ^CException in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded As a consequence of this change, I had to remove eager clearing of the uniques types cache. The comment in the code elaborates further.
1 parent 4e4c151 commit 00e11ff

File tree

2 files changed

+12
-7
lines changed

2 files changed

+12
-7
lines changed

src/reflect/scala/reflect/internal/SymbolTable.scala

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -344,16 +344,18 @@ abstract class SymbolTable extends macros.Universe
344344

345345
// Weak references so the garbage collector will take care of
346346
// letting us know when a cache is really out of commission.
347-
private val caches = WeakHashSet[Clearable]()
347+
import java.lang.ref.WeakReference
348+
private var caches = List[WeakReference[Clearable]]()
348349

349350
def recordCache[T <: Clearable](cache: T): T = {
350-
caches += cache
351+
caches ::= new WeakReference(cache)
351352
cache
352353
}
353354

354355
def clearAll() = {
355356
debuglog("Clearing " + caches.size + " caches.")
356-
caches foreach (_.clear)
357+
caches foreach (ref => Option(ref.get).foreach(_.clear))
358+
caches = caches.filterNot(_.get == null)
357359
}
358360

359361
def newWeakMap[K, V]() = recordCache(mutable.WeakHashMap[K, V]())
@@ -364,9 +366,9 @@ abstract class SymbolTable extends macros.Universe
364366
val NoCached: T = null.asInstanceOf[T]
365367
var cached: T = NoCached
366368
var cachedRunId = NoRunId
367-
caches += new Clearable {
369+
recordCache(new Clearable {
368370
def clear(): Unit = cached = NoCached
369-
}
371+
})
370372
() => {
371373
if (currentRunId != cachedRunId || cached == NoCached) {
372374
cached = f

src/reflect/scala/reflect/internal/Types.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ package reflect
88
package internal
99

1010
import scala.collection.{ mutable, immutable, generic }
11-
import generic.Clearable
1211
import scala.ref.WeakReference
1312
import mutable.ListBuffer
1413
import Flags._
@@ -3660,7 +3659,11 @@ trait Types
36603659
if (Statistics.canEnable) Statistics.incCounter(rawTypeCount)
36613660
if (uniqueRunId != currentRunId) {
36623661
uniques = util.WeakHashSet[Type](initialUniquesCapacity)
3663-
perRunCaches.recordCache(uniques)
3662+
// JZ: We used to register this as a perRunCache so it would be cleared eagerly at
3663+
// the end of the compilation run. But, that facility didn't actually clear this map (SI-8129)!
3664+
// When i fixed that bug, run/tpeCache-tyconCache.scala started failing. Why was that?
3665+
// I've removed the registration for now. I don't think its particularly harmful anymore
3666+
// as a) this is now a weak set, and b) it is discarded completely before the next run.
36643667
uniqueRunId = currentRunId
36653668
}
36663669
(uniques findEntryOrUpdate tp).asInstanceOf[T]

0 commit comments

Comments
 (0)