2.x: BoundedReplayBuffer temporarily leaks memory
See original GitHub issueWhen using Observable.replay(N)
, the BoundedReplayBuffer
keeps up to N+1
items in its buffer. When using replay(1)
for example, it keeps a reference to the most recent item, but also the previous, stale item.
Take for example this trivial snippet of code that provides an available Android View
as a stream:
val eventsSubject = BehaviorSubject.create<Event<View>>()
val view: Observable<Option<View>> = eventsSubject
.map { event ->
when(event) {
is Attached<View> -> Option.just(event.view)
is Detached -> Option.empty()
}
}
.replay(1).refCount()
The replay(1)
call suggests a single value is cached, but the implementation keeps a strong reference to the previous item as well.
Since this happens as a hidden side effect and rather counter intuitively, it is easy to accidentally leak memory – even when the client code seems to be careful about it. Especially with Android Views referencing Activity contexts this is problematic.
https://github.com/ReactiveX/RxJava/pull/5282 proposed a fix for this at the cost of an extra Node
allocation, which turned out to be unwanted.
The proposed alternative there refers to RxJava2Extensions#cacheLast, but this only emits the very last item, not intermediates.
Issue Analytics
- State:
- Created 4 years ago
- Comments:6 (4 by maintainers)
Item lifecycle is not part of the Reactive Streams model, thus the operator only guarantees one gets up to a number of items when subscribing to cold replay. People have been using LeakCanary to detect such cases and most likely reworked their flow to not use replay.
Closing via #6532 for 3.x.