Skip to content

Instantly share code, notes, and snippets.

@dragos
Last active December 11, 2015 15:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dragos/4622507 to your computer and use it in GitHub Desktop.
Save dragos/4622507 to your computer and use it in GitHub Desktop.
AttachmentsLeakTest
/**
* Test that default arguments don't leak
*/
class Foo(
x: Int = 100,
y: Int = 101,
z: Int = 102
str1: String = "str1",
str2: String = "str1",
str3: String = "str1",
str4: String = "str1",
str5: String = "str1",
str6: String = "str1",
str7: String = "str1",
str8: String = "str1",
str9: String = "str1",
str10: String = "str1",
str11: String = "str1",
str12: String = "str1",
str13: String = "str1",
str14: String = "str1",
str15: String = "str1",
str16: String = "str1")
class Bar(
x: Int = 100,
y: Int = 101,
z: Int = 102
str1: String = "str1",
str2: String = "str1",
str3: String = "str1",
str4: String = "str1",
str5: String = "str1",
str6: String = "str1",
str7: String = "str1",
str8: String = "str1",
str9: String = "str1",
str10: String = "str1",
str11: String = "str1",
str12: String = "str1",
str13: String = "str1",
str14: String = "str1",
str15: String = "str1",
str16: String = "str1")
class Baz(
x: Int = 100,
y: Int = 101,
z: Int = 102
str1: String = "str1",
str2: String = "str1",
str3: String = "str1",
str4: String = "str1",
str5: String = "str1",
str6: String = "str1",
str7: String = "str1",
str8: String = "str1",
str9: String = "str1",
str10: String = "str1",
str11: String = "str1",
str12: String = "str1",
str13: String = "str1",
str14: String = "str1",
str15: String = "str1",
str16: String = "str1")
import java.io.PrintWriter
import java.io.FileOutputStream
import java.util.Calendar
import scala.tools.nsc.interactive.tests._
import scala.tools.nsc.util._
import scala.tools.nsc.io._
import scala.tools.nsc.doc
/** memory leak test
*/
object Test extends InteractiveTest {
final val mega = 1024 * 1024
// override val settings: doc.Settings = docSettings
override def main(args: Array[String]): Unit = memoryConsumptionTest()
def batchSource(name: String) =
new BatchSourceFile(AbstractFile.getFile(name))
def memoryConsumptionTest() {
val N = 50
val filename = "usedmem-%tF.txt".format(Calendar.getInstance.getTime)
loadSources()
val usedMem = for (i <- 1 to N) yield {
val usedMem = withGC {
typeCheck()
}
usedMem / mega // report size in MB
}
dumpDataToFile(filename, usedMem)
println("Beginning at \t%d MB".format(usedMem.head))
println("Ending at \t%d MB".format(usedMem.last))
// drop the first two measurements, since the compiler needs some memory when initializing
val (a, b) = linearModel((3L to N).toSeq, usedMem.drop(2))
println("LinearModel: constant: %.4f\tslope:%.4f".format(a, b))
println(compiler.getClass.getDeclaredField("scala$tools$nsc$interactive$Global$$currentTopLevelSyms"))
val topLevelSymsField = compiler.getClass.getDeclaredField("scala$tools$nsc$interactive$Global$$currentTopLevelSyms")
topLevelSymsField.setAccessible(true)
val topLevelSyms = topLevelSymsField.get(compiler).asInstanceOf[collection.mutable.LinkedHashSet[compiler.Symbol]]
for (sym <- topLevelSyms) {
import scala.reflect.macros.Attachments
var att: Attachments = sym.attachments
if (!att.isInstanceOf[Position]) {
println(att.getClass)
val attOuter = att.getClass.getDeclaredField("$outer")
attOuter.setAccessible(true)
var len = 0
while (!att.isInstanceOf[Position]) {
len += 1
att = attOuter.get(att).asInstanceOf[Attachments]
}
println("Sym: %s \tlen: %d".format(sym, len))
}
}
if (b > 1.0)
println("Rate of memory consumption is alarming! %.4f MB/run".format(b))
else
println("No leaks detected.")
}
private def typeCheck() = {
askReload(sourceFiles)
askLoadedTyped(sourceFiles.head).get // block until it's here
}
private def dumpDataToFile(filename: String, usedMem: Seq[Long]) {
val outputFile = new PrintWriter(new FileOutputStream(filename))
outputFile.println("\tusedMem")
for ((dataPoint, i) <- usedMem.zipWithIndex) {
outputFile.println("%d\t%d".format(i, dataPoint))
}
outputFile.close()
}
/** Return the linear model of these values, (a, b). First value is the constant factor,
* second value is the slope, i.e. `y = a + bx`
*
* The linear model of a set of points is a straight line that minimizes the square distance
* between the each point and the line.
*
* See: http://en.wikipedia.org/wiki/Simple_linear_regression
*/
def linearModel(xs: Seq[Long], ys: Seq[Long]): (Double, Double) = {
require(xs.length == ys.length)
def mean(v: Seq[Long]): Double = v.sum.toDouble / v.length
val meanXs = mean(xs)
val meanYs = mean(ys)
val beta = (mean((xs, ys).zipped.map(_ * _)) - meanXs * meanYs) / (mean(xs.map(x => x * x)) - meanXs * meanXs)
val alfa = meanYs - beta * meanXs
(alfa, beta)
}
/** Run the given closure and return the amount of used memory at the end of its execution.
*
* Runs the GC before and after the execution of `f'.
*/
def withGC(f: => Unit): Long = {
val r = Runtime.getRuntime
System.gc()
f;
System.gc()
r.totalMemory() - r.freeMemory()
}
}
Beginning at 22 MB
Ending at 23 MB
LinearModel: constant: 21.6618 slope:0.0206
private final scala.collection.mutable.LinkedHashSet scala.tools.nsc.interactive.Global.scala$tools$nsc$interactive$Global$$currentTopLevelSyms
class scala.reflect.macros.Attachments$NonemptyAttachments
Sym: object Foo len: 101
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment