-
Notifications
You must be signed in to change notification settings - Fork 7.6k
Description
Consumers passed to any of the .subscribe()
overloads have their captured variables leaked longer than they have to be.
Taking Single as an example, though this happens for any of the RxJava types:
val disposable = someSingle
.doStuff()
.subscribe { _ ->
println(someLargeObject.status)
}
compositeDisposable.add(disposable)
If the above stream finishes quickly and there are no longer any other references to someLargeObject, the consumer passed in to the subscribe function which captures the someLargeObject will not be released until the compositeDisposable is cleared.
A way around this is to just change the RxJava stream to not capture anything in the subscribe consumer, but instead capture it earlier and pass it along through the stream:
val disposable = someSingle
.doStuff()
.map { it to someLargeObject } // capture here isntead
.subscribe { (_, obj) ->
println(obj.status)
}
compositeDisposable.add(disposable)
This works because once the final observer receives onSuccess, it clears its reference to its upstream disposable, which includes the map which contains the closure that captured someLargeObject. I understand that generally, good RxJava design includes "keeping everything within the stream", in other words capturing as little as possible by only operating on objects in the stream. However, this can sometimes be difficult to achieve.
A simple fix for this is to have the terminal observer, in this case ConsumerSingleObserver
, null out its references to its onSuccess and onError consumers as soon as it transitions to disposed.
This would solve a rather common memory leak vector that many RxJava developers unknowingly fall victim to.