diff options
-rw-r--r-- | src/partest/scala/tools/partest/MemoryTest.scala | 38 | ||||
-rw-r--r-- | src/reflect/scala/reflect/runtime/SynchronizedTypes.scala | 24 | ||||
-rw-r--r-- | test/files/run/reflection-mem-glbs.scala | 13 | ||||
-rw-r--r-- | test/files/run/reflection-mem-tags.scala | 17 |
4 files changed, 91 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 diff --git a/test/files/run/reflection-mem-glbs.scala b/test/files/run/reflection-mem-glbs.scala new file mode 100644 index 0000000000..3f29a914bc --- /dev/null +++ b/test/files/run/reflection-mem-glbs.scala @@ -0,0 +1,13 @@ +import scala.tools.partest.MemoryTest + +trait A { type T <: A } +trait B { type T <: B } + +object Test extends MemoryTest { + override def maxDelta = 10 + override def calcsPerIter = 50000 + override def calc() { + import scala.reflect.runtime.universe._ + glb(List(typeOf[A], typeOf[B])) + } +}
\ No newline at end of file diff --git a/test/files/run/reflection-mem-tags.scala b/test/files/run/reflection-mem-tags.scala new file mode 100644 index 0000000000..8815e7dcd8 --- /dev/null +++ b/test/files/run/reflection-mem-tags.scala @@ -0,0 +1,17 @@ +import scala.tools.partest.MemoryTest + +trait A { type T <: A } +trait B { type T <: B } + +object Test extends MemoryTest { + override def maxDelta = 10 + override def calcsPerIter = 100000 + override def calc() { + import scala.reflect.runtime.universe._ + def foo = { + class A { def x = 2; def y: A = new A } + weakTypeOf[A { def z: Int }] + } + foo + } +}
\ No newline at end of file |