summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorIulian Dragos <jaguarul@gmail.com>2009-09-28 16:10:38 +0000
committerIulian Dragos <jaguarul@gmail.com>2009-09-28 16:10:38 +0000
commit449f2a7473cb2c390981c1dead2da86a5129feae (patch)
tree461ea0311ba7e4e3745618ee00dfcd972177d304 /src
parent4231751ecf81819f9e973df817c884158014fa51 (diff)
downloadscala-449f2a7473cb2c390981c1dead2da86a5129feae.tar.gz
scala-449f2a7473cb2c390981c1dead2da86a5129feae.tar.bz2
scala-449f2a7473cb2c390981c1dead2da86a5129feae.zip
Lazy fields null out fields that are used only ...
Lazy fields null out fields that are used only in their initializer. When the lazy value is forced, it will null out all private fields that are used only by the current lazy value. This should reduce memory leaks, see #720
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/GenICode.scala2
-rw-r--r--src/compiler/scala/tools/nsc/transform/Mixin.scala95
2 files changed, 85 insertions, 12 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
index da067ae1d9..b9d74ab686 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
@@ -69,7 +69,7 @@ abstract class GenICode extends SubComponent {
override def apply(unit: CompilationUnit): Unit = {
this.unit = unit
unit.icode.clear
- log("Generating icode for " + unit)
+ informProgress("Generating icode for " + unit)
gen(unit.body)
this.unit = null
}
diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala
index 71caf89010..d3c3a86f4c 100644
--- a/src/compiler/scala/tools/nsc/transform/Mixin.scala
+++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala
@@ -9,9 +9,8 @@ package transform
import symtab._
import Flags._
-import scala.collection.mutable.ListBuffer
import scala.tools.nsc.util.{Position,NoPosition}
-import scala.collection.mutable.HashMap
+import collection.mutable.{ListBuffer, HashMap}
abstract class Mixin extends InfoTransform with ast.TreeDSL {
import global._
@@ -364,6 +363,50 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
tp
}
+ import scala.collection._
+
+ /** Return a map of single-use fields to the lazy value that uses them during initialization.
+ * Each field has to be private and defined in the enclosing class, and there must
+ * be exactly one lazy value using it.
+ *
+ * Such fields will be nulled after the initializer has memoized the lazy value.
+ */
+ def singleUseFields(templ: Template): collection.Map[Symbol, List[Symbol]] = {
+ val usedIn = new mutable.HashMap[Symbol, List[Symbol]] {
+ override def default(key: Symbol) = Nil
+ }
+
+ object SingleUseTraverser extends Traverser {
+ override def traverse(tree: Tree) {
+ tree match {
+ case Assign(lhs, rhs) => traverse(rhs) // assignments don't count
+ case _ =>
+ if (tree.hasSymbol && tree.symbol != NoSymbol) {
+ val sym = tree.symbol
+ if ((sym.hasFlag(ACCESSOR) || (sym.isTerm && !sym.isMethod))
+ && sym.hasFlag(PRIVATE)
+ && !(currentOwner.isGetter && currentOwner.accessed == sym) // getter
+ && !definitions.isValueClass(sym.tpe.resultType.typeSymbol)
+ && sym.owner == templ.symbol.owner
+ && !tree.isDef) {
+ log("added use in: " + currentOwner + " -- " + tree)
+ usedIn(sym) ::= currentOwner
+
+ }
+ }
+ super.traverse(tree)
+ }
+ }
+ }
+ SingleUseTraverser(templ)
+ log("usedIn: " + usedIn)
+ usedIn filter { pair =>
+ val member = pair._2.head
+ (member.isValue
+ && member.hasFlag(LAZY)
+ && pair._2.tail.isEmpty) }
+ }
+
// --------- term transformation -----------------------------------------------
protected def newTransformer(unit: CompilationUnit): Transformer =
@@ -384,6 +427,9 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
private var localTyper: erasure.Typer = _
private def typedPos(pos: Position)(tree: Tree) = localTyper typed { atPos(pos)(tree) }
+ /** Map lazy values to the fields they should null after initialization. */
+ private var lazyValNullables: mutable.MultiMap[Symbol, Symbol] = _
+
import scala.collection._
/** Map a field symbol to a unique integer denoting its position in the class layout.
@@ -612,17 +658,27 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
* where bitmap$n is an int value acting as a bitmap of initialized values. It is
* the 'n' is (offset / 32), the MASK is (1 << (offset % 32)).
*/
- def mkLazyDef(clazz: Symbol, init: List[Tree], retVal: Tree, offset: Int): Tree = {
+ def mkLazyDef(clazz: Symbol, lzyVal: Symbol, init: List[Tree], retVal: Tree, offset: Int): Tree = {
+ def nullify(sym: Symbol): Tree = {
+ val sym1 = if (sym.hasFlag(ACCESSOR)) sym.accessed else sym
+ Select(This(clazz), sym1) === LIT(null)
+ }
+
+
val bitmapSym = bitmapFor(clazz, offset)
val mask = LIT(1 << (offset % FLAGS_PER_WORD))
def cond = mkTest(clazz, mask, bitmapSym, true)
+ val nulls = (lazyValNullables(lzyVal).toList.sort(_.id < _.id) map nullify)
def syncBody = init ::: List(mkSetFlag(clazz, offset), UNIT)
+ log("nulling fields inside " + lzyVal + ": " + nulls)
val result =
- IF (cond) THEN gen.mkSynchronized(
- gen mkAttributedThis clazz,
- IF (cond) THEN BLOCK(syncBody: _*) ENDIF
- ) ENDIF
+ IF (cond) THEN BLOCK(
+ (gen.mkSynchronized(
+ gen mkAttributedThis clazz,
+ IF (cond) THEN BLOCK(syncBody: _*) ENDIF
+ )
+ :: nulls): _*) ENDIF
typedPos(init.head.pos)(BLOCK(result, retVal))
}
@@ -653,10 +709,10 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
if sym.hasFlag(LAZY) && rhs != EmptyTree && !clazz.isImplClass =>
assert(fieldOffset.isDefinedAt(sym))
val rhs1 = if (sym.tpe.resultType.typeSymbol == UnitClass)
- mkLazyDef(clazz, List(rhs), UNIT, fieldOffset(sym))
+ mkLazyDef(clazz, sym, List(rhs), UNIT, fieldOffset(sym))
else {
val Block(stats, res) = rhs
- mkLazyDef(clazz, stats, Select(This(clazz), res.symbol), fieldOffset(sym))
+ mkLazyDef(clazz, sym, stats, Select(This(clazz), res.symbol), fieldOffset(sym))
}
treeCopy.DefDef(stat, mods, name, tp, vp, tpt, rhs1)
@@ -790,13 +846,13 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
if (sym.hasFlag(LAZY) && sym.isGetter) {
val rhs1 =
if (sym.tpe.resultType.typeSymbol == UnitClass)
- mkLazyDef(clazz, List(Apply(staticRef(initializer(sym)), List(gen.mkAttributedThis(clazz)))), UNIT, fieldOffset(sym))
+ mkLazyDef(clazz, sym, List(Apply(staticRef(initializer(sym)), List(gen.mkAttributedThis(clazz)))), UNIT, fieldOffset(sym))
else {
val assign = atPos(sym.pos) {
Assign(Select(This(sym.accessed.owner), sym.accessed) /*gen.mkAttributedRef(sym.accessed)*/ ,
Apply(staticRef(initializer(sym)), gen.mkAttributedThis(clazz) :: Nil))
}
- mkLazyDef(clazz, List(assign), Select(This(clazz), sym.accessed), fieldOffset(sym))
+ mkLazyDef(clazz, sym, List(assign), Select(This(clazz), sym.accessed), fieldOffset(sym))
}
rhs1
} else if (sym.getter(sym.owner).tpe.resultType.typeSymbol == UnitClass) {
@@ -855,6 +911,22 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
stats1
}
+ private def nullableFields(templ: Template) = {
+ val nullables = new mutable.HashMap[Symbol, mutable.Set[Symbol]] with mutable.MultiMap[Symbol, Symbol] {
+ override def default(key: Symbol) = mutable.Set.empty
+ }
+
+ // if there are no lazy fields, take the fast path and save a traversal of the whole AST
+ if (templ.symbol.owner.info.decls.exists(_.hasFlag(LAZY))) {
+ // check what fields can be nulled for
+ val uses = singleUseFields(templ)
+ for ((field, users) <- uses; lazyFld <- users) {
+ nullables.addBinding(lazyFld, field)
+ }
+ }
+ nullables
+ }
+
/** The transform that gets applied to a tree after it has been completely
* traversed and possible modified by a preTransform.
* This step will
@@ -884,6 +956,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
// change parents of templates to conform to parents in the symbol info
val parents1 = currentOwner.info.parents map (t => TypeTree(t) setPos tree.pos)
+ lazyValNullables = nullableFields(tree.asInstanceOf[Template])
// add all new definitions to current class or interface
val body1 = addNewDefs(currentOwner, body)