diff options
author | Maxim Valyanskiy <max.valjanski@gmail.com> | 2014-10-17 23:19:54 +0400 |
---|---|---|
committer | Maxim Valyanskiy <max.valjanski@gmail.com> | 2014-11-27 09:36:26 +0300 |
commit | 0bb8a139e9c46e3e1b4bd7af5dd11cb689ebbe8e (patch) | |
tree | 4141ea2586e252202447603071bb5fa348ce7b76 | |
parent | 3ac6eabae685f08fb349ddab16cbaa1c6e97984b (diff) | |
download | scala-0bb8a139e9c46e3e1b4bd7af5dd11cb689ebbe8e.tar.gz scala-0bb8a139e9c46e3e1b4bd7af5dd11cb689ebbe8e.tar.bz2 scala-0bb8a139e9c46e3e1b4bd7af5dd11cb689ebbe8e.zip |
SI-8924 don't hold reference to list in iterator
Current implementation of LinearSeqLike.iterator holds
reference to complete sequence. This prevent garbage collecting
of List elements while we keep reference to iterator somewhere.
This commit removes reference from Iterator implementation. This
allow garbage collection of List while we iterating over its
elements (when there is no other references to List in our program).
-rw-r--r-- | src/library/scala/collection/LinearSeqLike.scala | 14 | ||||
-rw-r--r-- | test/junit/scala/collection/immutable/ListTest.scala | 49 |
2 files changed, 59 insertions, 4 deletions
diff --git a/src/library/scala/collection/LinearSeqLike.scala b/src/library/scala/collection/LinearSeqLike.scala index ff7985bf0d..6fb452797e 100644 --- a/src/library/scala/collection/LinearSeqLike.scala +++ b/src/library/scala/collection/LinearSeqLike.scala @@ -58,12 +58,18 @@ trait LinearSeqLike[+A, +Repr <: LinearSeqLike[A, Repr]] extends SeqLike[A, Repr val result = these.head; these = these.tail; result } else Iterator.empty.next() - /** Have to clear `these` so the iterator is exhausted like - * it would be without the optimization. - */ override def toList: List[A] = { + /* Have to clear `these` so the iterator is exhausted like + * it would be without the optimization. + * + * Calling "newBuilder.result()" in toList method + * prevents original seq from garbage collection, + * so we use these.take(0) here. + * + * Check SI-8924 for details + */ val xs = these.toList - these = newBuilder.result() + these = these.take(0) xs } } diff --git a/test/junit/scala/collection/immutable/ListTest.scala b/test/junit/scala/collection/immutable/ListTest.scala new file mode 100644 index 0000000000..1006801029 --- /dev/null +++ b/test/junit/scala/collection/immutable/ListTest.scala @@ -0,0 +1,49 @@ +package scala.collection.immutable + +import org.junit.{Assert, Test} +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +import scala.ref.WeakReference + +@RunWith(classOf[JUnit4]) +class ListTest { + /** + * Test that empty iterator does not hold reference + * to complete List + */ + @Test + def testIteratorGC(): Unit = { + var num = 0 + var emptyIterators = Seq.empty[(Iterator[Int], WeakReference[List[Int]])] + + do { + val list = List.fill(10000)(num) + val ref = WeakReference(list) + + val i = list.iterator + + while (i.hasNext) i.next() + + emptyIterators = (i, ref) +: emptyIterators + + num+=1 + } while (emptyIterators.forall(_._2.get.isDefined) && num<1000) + + // check something is result to protect from JIT optimizations + for ((i, _) <- emptyIterators) { + Assert.assertTrue(i.isEmpty) + } + + // await gc up to ~5 seconds + var forceLoops = 50 + while (emptyIterators.forall(_._2.get.isDefined) && forceLoops>0) { + System.gc() + Thread.sleep(100) + forceLoops -= 1 + } + + // real assertion + Assert.assertTrue(emptyIterators.exists(_._2.get.isEmpty)) + } +} |