Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reflection leaks memory #6412

Open
scabug opened this issue Sep 20, 2012 · 6 comments
Open

Reflection leaks memory #6412

scabug opened this issue Sep 20, 2012 · 6 comments
Assignees
Milestone

Comments

@scabug
Copy link

scabug commented Sep 20, 2012

http://groups.google.com/group/scala-internals/browse_thread/thread/bc60a6909accd18d

@scabug
Copy link
Author

scabug commented Sep 20, 2012

Imported From: https://issues.scala-lang.org/browse/SI-6412?orig=1
Reporter: @xeno-by
Attachments:

@scabug
Copy link
Author

scabug commented Sep 21, 2012

@jsuereth said:
If you want a "Test memory does not exceed XM" type partest feature, open a blocker ticket for me.

@scabug
Copy link
Author

scabug commented Sep 21, 2012

@xeno-by said:
I don't know yet, but thanks!

@scabug
Copy link
Author

scabug commented Sep 25, 2012

@xeno-by said (edited on Sep 25, 2012 11:19:53 AM UTC):
This should significantly reduce the leaks: scala/scala@291d1f0. The rest will be fixed in subsequent point releases.

@scabug
Copy link
Author

scabug commented Nov 21, 2013

@retronym said:
Marking as critical so that we look at this again for 2.11

@retronym
Copy link
Member

retronym commented Oct 10, 2019

Here's a test that shows a leak in Toolbox.define.

import scala.reflect.runtime.universe._
import scala.reflect.runtime.currentMirror
import scala.tools.reflect.ToolBox

object Test  {
  def main(args: Array[String]): Unit = {
    for (i <- 1 to 1024) {
      println(i)
      val tb = currentMirror.mkToolBox()
      val sym = tb.define(q"""object Test { object C { val data = new Array[Byte](16 * 1024 * 1024) }; class C; def main(args: Array[String]) { C.data }}""")
      tb.eval(q"""$sym.main(null)""")
      tb
    }
  }
}

The leak is through s.r.runtime.universe -> definitions -> RootClass -> infos -> decls, as the wrapper classes are entered there but never removed.

6 = {SynchronizedSymbols$SynchronizedSymbol$$anon$13@8241} "package __wrapper$1$9ac0dd63cec64c529019147b8e1b130b"
7 = {SynchronizedSymbols$SynchronizedSymbol$$anon$13@8242} "package __wrapper$1$bfe831869b40477a84b2f17ca3a141f3"
8 = {SynchronizedSymbols$SynchronizedSymbol$$anon$13@8243} "package __wrapper$1$0eecdf17aaa44fb9a500ed65a7d2077b"
9 = {SynchronizedSymbols$SynchronizedSymbol$$anon$13@8244} "package __wrapper$1$b21daff6f18c4545b22aec5481ffc2ed"
10 = {SynchronizedSymbols$SynchronizedSymbol$$anon$13@8245} "package __wrapper$1$cc34245e94bb406ba71d1fe2a60ee7fd"
11 = {SynchronizedSymbols$SynchronizedSymbol$$anon$13@8246} "package __wrapper$1$9daafbd75ee44119b9e69cc296722030"
12 = {SynchronizedSymbols$SynchronizedSymbol$$anon$13@8247} "package __wrapper$1$915b5355ba35463a914c07f8b77dbf59"
13 = {SynchronizedSymbols$SynchronizedSymbol$$anon$13@8248} "package __wrapper$1$c26724bb96914c89be4bdedabe66e593"

Workarounds:

  • avoid define in favour of Toolbox#compile
  • use the real compiler (scala.tools.nsc.Global) with a real classpath rather the Toolbox.
  • manually avoid the leak after calling define
import scala.reflect.runtime.universe
import scala.reflect.runtime.universe._
import scala.reflect.runtime.currentMirror
import scala.tools.reflect.ToolBox

object Test {
  def main(args: Array[String]): Unit = {
    for (i <- 1 to 1024) {
      println(i)
      val tb = currentMirror.mkToolBox()
      // leaks this toolbox via synthetic "wrapper" packages entered in RootClass!
      val sym = tb.define(q"""object Test { object C { val data = new Array[Byte](16 * 1024 * 1024) }; class C; def main(args: Array[String]) { C.data }}""")
      tb.eval(q"""$sym.main(null)""")
      // workaround
      unlinkFromRootOwner(sym)

      // This version doesn't leak
      // tb.eval(q"""{object Test { object C { val data = new Array[Byte](16 * 1024 * 1024) }; class C; def main(args: Array[String]) { C.data }}; Test.main(null)}""")
    }
    println("sleeping"); Thread.sleep(120 * 1000)
  }
  def unlinkFromRootOwner(sym0: Symbol): Unit = {
    val symtab = universe.asInstanceOf[scala.reflect.internal.SymbolTable]
    val sym = sym0.asInstanceOf[symtab.Symbol]
    val symToRemove = sym.ownerChain.dropRight(1).last.sourceModule
    val scope = symToRemove.owner.info.decls
    scope.asInstanceOf[{def syncLockSynchronized[T](o: => T): T}].syncLockSynchronized {
      println(s"unlinking $symToRemove")
      scope.unlink(symToRemove)
    }
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants