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

This comment has been minimized.

Copy link
Author

@scabug scabug commented Sep 20, 2012

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

@scabug

This comment has been minimized.

Copy link
Author

@scabug 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

This comment has been minimized.

Copy link
Author

@scabug scabug commented Sep 21, 2012

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

@scabug

This comment has been minimized.

Copy link
Author

@scabug 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

This comment has been minimized.

Copy link
Author

@scabug scabug commented Nov 21, 2013

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

@scabug scabug added this to the Backlog milestone Apr 7, 2017
@SethTisue SethTisue removed the critical label Jul 27, 2017
@retronym retronym self-assigned this Oct 10, 2019
@retronym

This comment has been minimized.

Copy link
Member

@retronym 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
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
4 participants
You can’t perform that action at this time.