How to leak memory with Subscriptions in RxJava
There are plenty of great how-to articles about RxJava. It does simplify things significantly when working with Android framework but be careful because simplification may have its own pitfalls. In the following parts, you are going to explore one of them and see how easy it is to create a memory leak with RxJava’s
Solving simple task
Imagine that your manager called in and asked you to create a widget which displays a random movie title. It has to be based on some external recommendation service. This widget should display a movie title on user’s demand or it could do it on its own. The manager also wants this widget to be able to store some information related to user’s interaction with it.
MVP-based approach is one of the many ways to do this. You create a simple view containing both
TextView widgets. The
RecommendedMovieUseCase handles providing a random movie title.
Presenter connects to a use case and displays a title on a view. Saving presenter’s state is implemented by keeping it in memory even when your Activity is being recreated (in so-called
Here is how your
Presenter looks like. For the purpose of this article, let’s assume that you want to store a flag indicating whether the user has tapped the title.
The widget will be added to a purple container when the user asks for a recommendation. It will be removed after the user decides to clear it.
Everything seems to be working fine for now.
To be safer, we decided to initialize StrictMode in debug builds.
We start to play around with our app and try to rotate our device a couple of times. Suddenly, a log message appears.
This does not sound right. You can try dumping current memory state and take a deeper look at the problem:
There is your culprit marked with the blue font. For some reason, there is still an instance of
MovieSuggestionView holding a reference to an old
But why? You have unsubscribed from your background job and also cleared the reference to a
MovieSuggestionView when dropping the view from your
Presenter. Where is this leak coming from?
Searching for a leak
By storing a reference to a
Subscription, you are actually storing an instance of an
ActionSubscriber<T>, which looks like this:
onCompleted fields being final there is no clean way to nullify them. The problem is that calling
unsubscribe() on a
Subscriber only marks it as unsubscribed (and couple of things more, but it is not important in our case).
For those who are wondering from where this
ActionSubscriber object comes from, take a look at the
subscribe method definition:
Further analysis of the memory dump proves that MovieSuggestionView reference is indeed still kept inside of
To understand the problem better, let’s dig a little bit deeper and see what happens after your code gets compiled.
=> ls -1 app/build/intermediates/classes/debug/me/scana/subscriptionsleak
You can see that in addition to your main
Presenter class, you get two additional class files, one for each of anonymous
Action1<> classes that you introduced.
Let’s check out what is happening inside of one of those anonymous classes using a handy javap tool:
=> javap -c Presenter\$1
You might have heard that an anonymous class holds an implicit reference to an outer class. It turns out that anonymous classes also capture all of the variables that you use inside of them.
Because of this, by keeping a reference to a
Subscription object, you effectively keep references to those anonymous classes that you used to handle the movie title result. They keep the reference to a view that you wanted to do something with and there is your leak.
You know what is wrong with our current solution. So, how do you fix it?
It is quite easy.
You can set our
Subscription object to
Subscription.empty(), thus clearing up a reference to an old
There is also a
CompositeSubscription class which allows to store multiple
Subscription objects and performs
unsubscribe() on them. This should relieve us from storing a
Subscription reference directly. Keep in mind, though, that this will not yet solve your problem. The references are still going to be kept inside of
Fortunately, there is a
clear() method available, which unsubscribes everything and then clears up the references. It also allows you to reuse a
CompositeSubscription object as opposed to
unsubscribe() which renders your object unusable.
Here is fixed
Presenter class with one of the aforementioned methods implemented:
It is worth adding that you can actually solve this problem in many different ways. Always keep in mind that there is no silver bullet for every issue that you come upon.
To sum things up:
Subscriptionobjects hold final references to your callbacks. Your callbacks can hold references to your Android’s lifecycle-tied objects. They both can leak memory when not treated with care
- You can use tools like StrictMode, javap, HPROF Viewer to find and analyze the source of leaks. I did not mention it in the article, but you can also check out the LeakCanary library from Square.
- Digging into libraries that you use on a daily basis helps a lot with solving potential problems that may arise
I hope that you have enjoyed the article and maybe even learned something
new from it.
Please do not hesitate to ask a question and share your own ideas on working with RxJava in the comment section below!
You can also reach me on my Twitter’s account :)
You may also find interesting:
Grab the code from this article:
Issue about final references in subscribers on GitHub:
Android’s StrictMode: https://developer.android.com/reference/android/os/StrictMode.html
javap, The Java Class File Disassembler