summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEugene Burmako <xeno.by@gmail.com>2012-09-23 18:58:50 +0200
committerEugene Burmako <xeno.by@gmail.com>2012-09-24 14:28:58 +0200
commit291d1f033a790e97298210de29f09a9796406ae3 (patch)
tree4e3e6d564f1e3c7877fa512d867389eddc2fab16 /src
parent6ba05fdf09e8ff81dda1b946d9f89b2bba9ad33b (diff)
downloadscala-291d1f033a790e97298210de29f09a9796406ae3.tar.gz
scala-291d1f033a790e97298210de29f09a9796406ae3.tar.bz2
scala-291d1f033a790e97298210de29f09a9796406ae3.zip
SI-6412 fixes leaks in Types.uniques
This is the most blatant leak in reflection. There are others, but their impact is much smaller, therefore we'll fix them later, after 2.10.0-final. For more information, see https://issues.scala-lang.org/browse/SI-6412 and http://groups.google.com/group/scala-internals/browse_thread/thread/eabcf3d406dab8b2
Diffstat (limited to 'src')
-rw-r--r--src/partest/scala/tools/partest/MemoryTest.scala38
-rw-r--r--src/reflect/scala/reflect/runtime/SynchronizedTypes.scala24
2 files changed, 61 insertions, 1 deletions
diff --git a/src/partest/scala/tools/partest/MemoryTest.scala b/src/partest/scala/tools/partest/MemoryTest.scala
new file mode 100644
index 0000000000..58d25d2f01
--- /dev/null
+++ b/src/partest/scala/tools/partest/MemoryTest.scala
@@ -0,0 +1,38 @@
+package scala.tools.partest
+
+abstract class MemoryTest {
+ def maxDelta: Double
+ def calcsPerIter: Int
+ def calc(): Unit
+
+ def main(args: Array[String]) {
+ val rt = Runtime.getRuntime()
+ def memUsage() = {
+ import java.lang.management._
+ import scala.collection.JavaConverters._
+ val pools = ManagementFactory.getMemoryPoolMXBeans.asScala
+ pools.map(_.getUsage.getUsed).sum / 1000000d
+ }
+
+ val history = scala.collection.mutable.ListBuffer[Double]()
+ def stressTestIter() = {
+ var i = 0
+ while (i < calcsPerIter) { calc(); i += 1 }
+ 1 to 5 foreach (_ => rt.gc())
+ history += memUsage
+ }
+
+ 1 to 5 foreach (_ => stressTestIter())
+ val reference = memUsage()
+ 1 to 5 foreach (_ => stressTestIter())
+ 1 to 5 foreach (_ => rt.gc())
+ val result = memUsage()
+ history += result
+
+ val delta = result - reference
+ if (delta > maxDelta) {
+ println("FAILED")
+ history foreach (mb => println(mb + " Mb"))
+ }
+ }
+}
diff --git a/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala b/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala
index de029ca658..b9b140a2fd 100644
--- a/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala
+++ b/src/reflect/scala/reflect/runtime/SynchronizedTypes.scala
@@ -1,6 +1,9 @@
package scala.reflect
package runtime
+import scala.collection.mutable.WeakHashMap
+import java.lang.ref.WeakReference
+
/** This trait overrides methods in reflect.internal, bracketing
* them in synchronized { ... } to make them thread-safe
*/
@@ -11,7 +14,26 @@ trait SynchronizedTypes extends internal.Types { self: SymbolTable =>
private object uniqueLock
- override def unique[T <: Type](tp: T): T = uniqueLock.synchronized { super.unique(tp) }
+ private val uniques = WeakHashMap[Type, WeakReference[Type]]()
+ override def unique[T <: Type](tp: T): T = uniqueLock.synchronized {
+ // we need to have weak uniques for runtime reflection
+ // because unlike the normal compiler universe, reflective universe isn't organized in runs
+ // therefore perRunCaches can grow infinitely large
+ //
+ // despite that toolbox universes are decorated, toolboxes are compilers,
+ // i.e. they have their caches cleaned up automatically on per-run basis,
+ // therefore they should use vanilla uniques, which are faster
+ if (!isCompilerUniverse) {
+ val result = if (uniques contains tp) uniques(tp).get else null
+ if (result ne null) result.asInstanceOf[T]
+ else {
+ uniques(tp) = new WeakReference(tp)
+ tp
+ }
+ } else {
+ super.unique(tp)
+ }
+ }
class SynchronizedUndoLog extends UndoLog {
private val actualLock = new java.util.concurrent.locks.ReentrantLock