-
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Closed
Labels
Description
Seems like nested spies can cause memory leaks since such objects are kept in memory without purging. Not sure if it can be resolved at all. Should it be avoided? Is there a mention about this in docs? Anyway, the code speaks better and fortunately I’ve been able to create a self-contained sample.
BTW I can provide a .hprof
file if you are interested.
Versions
org.mockito:mockito-core:2.22.0
org.mockito:mockito-inline:2.22.0
java version "1.8.0_181"
Java(TM) SE Runtime Environment (build 1.8.0_181-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.181-b13, mixed mode)
Gradle
Heap is set to 64 MB.
tasks.withType<Test> {
maxHeapSize = "64m"
jvmArgs("-XX:+HeapDumpOnOutOfMemoryError")
failFast = true
}
$ ./gradlew :module:cleanTestDebugUnitTest :module:testDebugUnitTest --tests "com.github.sample.NestedSpiesMemoryLeakSpec"
Code
package com.github.sample
import com.jakewharton.rxrelay2.BehaviorRelay
import io.reactivex.functions.Consumer
import org.jetbrains.spek.api.Spek
import org.jetbrains.spek.api.dsl.it
import org.junit.platform.runner.JUnitPlatform
import org.junit.runner.RunWith
import org.mockito.Mockito
@RunWith(JUnitPlatform::class)
class NestedSpiesMemoryLeakSpec : Spek({
repeat(10_000) { iteration ->
it("iteration [$iteration]") {
Mockito.spy(Service())
}
}
}) {
class Service {
// Remove Mockito.spy and OOM disappears.
val value = Mockito.spy(Consumer<Int> {
// This closure keeps a reference to Service.
streams.size
})
// See at as a mass to fill the RAM.
val streams = (0..1_000).map { BehaviorRelay.create<Int>() }
}
}
> Task :module:testDebugUnitTest
java.lang.OutOfMemoryError: GC overhead limit exceeded
Dumping heap to java_pid23350.hprof ...
Heap dump file created [99857779 bytes in 0.356 secs]
com.github.sample.NestedSpiesMemoryLeakSpec > it iteration [187] STANDARD_ERROR
java.lang.OutOfMemoryError: GC overhead limit exceeded
at com.jakewharton.rxrelay2.BehaviorRelay.<init>(BehaviorRelay.java:99)
at com.jakewharton.rxrelay2.BehaviorRelay.create(BehaviorRelay.java:77)
at com.github.sample.NestedSpiesMemoryLeakSpec$Service.<init>(NestedSpiesMemoryLeakSpec.kt:32)
at com.github.sample.NestedSpiesMemoryLeakSpec$1$1$1.invoke(NestedSpiesMemoryLeakSpec.kt:17)
at com.github.sample.NestedSpiesMemoryLeakSpec$1$1$1.invoke(NestedSpiesMemoryLeakSpec.kt:12)
at org.jetbrains.spek.engine.Scope$Test.execute(Scope.kt:102)
at org.jetbrains.spek.engine.Scope$Test.execute(Scope.kt:80)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:105)
at org.junit.platform.engine.support.hierarchical.NodeTestTask$$Lambda$82/547193480.execute(Unknown Source)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:72)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:95)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:71)
com.github.sample.NestedSpiesMemoryLeakSpec > it iteration [187] FAILED
java.lang.OutOfMemoryError
> Task :module:testDebugUnitTest FAILED
Eclipse Memory Analyzer
Seems like this happens:
Service
is a spy.Service
contains aConsumer
, it is aspy
as well.Consumer
is a closure and has an implicit reference toService
.- Mockito keeps both spies and cannot remove them from memory since there is a cross-reference (I guess).
dmitry-novikov, iatsuk and bric3