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 MainActivity instance.

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 Disposable , you are actually storing an instance of an LambdaObserver<T> , which looks like this:

Definition of the LambdaObserver

Because of onNext , onError and onCompleted fields being final there is no clean way to nullify them. The problem is that calling dispose() on a LambdaObserver 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 LambdaObserver object comes from, take a look at the subscribe method definition:

One of few subscribe methods in Observable class

Further analysis of the memory dump proves that MovieSuggestionView reference is indeed still kept inside of onNext and onError fields.

View is still referenced by a LambdaObserver

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/javac/debug/classes/me/scana/subscriptionsleak ...

Presenter$1.class

Presenter$2.class

Presenter.class

...

You can see that in addition to your main Presenter class, you get two additional class files, one for each of anonymous Consumer<> classes that you introduced.

Let’s check out what is happening inside of one of those anonymous classes using a handy javap tool:

=> cd app/build/intermediates/javac/debug/classes/me/scana/subscriptionsleak

=> javap -c Presenter\$1

Disassembled Presenter$1 class

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 Disposable 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.

But what about Kotlin?

Well. To be honest, it turns out that it is almost the same. It seems pretty reasonable that you might write this piece of code:

Example written in Kotlin

If you give it the same javap treatment as you did before (please note how Kotlin classes are stored in a different place):

=> cd app/build/tmp/kotlin-classes/debug/me/scana/subscriptionsleak => javap -c LeakingPresenterKt\$showRecommendedMovieTitle\$1.class

this is what you are going to find out — an implicit reference to a view.

Kotlin’s anonymous class also holds an implicit reference

You know what is wrong with our current solution. So, how do you fix it?

It is quite easy.

You can set our Disposable object to Disposables.disposed() , thus clearing up a reference to an old LambdaObserver .

There is also a CompositeDisposable class which allows to store multiple Disposable objects and performs dispose() on them. This should relieve us from storing a Disposable reference directly. Keep in mind, though, that this will not yet solve your problem. The references are still going to be kept inside of CompositeDisposable .

Fortunately, there is a clear() method available, which unsubscribes everything and then clears up the references. It also allows you to reuse a CompositeDisposable object as opposed to dispose() which renders your object unusable.

Here is fixed Presenter class with one of the aforementioned methods implemented:

A NonLeakingPresenter example

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.

The actual root of this problem

In my previous article, Maciej Górski pointed out to me that the root cause of the problem is this part:

void showRecommendedMovieTitle(final MovieSuggestionView view)

If you remove that method parameter and simply use view field directly (with proper null checks), you will find out that an implicit reference to your view is no longer present:

LeakingPresenter without an implicit view reference

You are now accessing view field through an implicit presenter object reference. Without a direct reference to a view, it is now harder for you to create a memory leak.

This is all because with a method parameter, the compiler is unable to figure real source of your view. Without knowledge of the source, it has to keep an implicit reference to it instead of using it through your Presenter instance.

While it seems like a trivial thing you can probably see that it might cause much bigger issues. It is good to be aware of potential side effect that a compiler might cause without any warning.

To sum things up:

Disposable objects 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.

objects 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. If compiler is not able to figure out source of your object in a method call, there is a chance that your object will be kept inside of an anonymous class, without you even knowing.

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

Thanks!

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 RxJava2 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:

https://github.com/scana/subscriptions-leak-example

Please, refer to master-rxjava2 branch for the latest version or master branch for a version written in RxJava 1.x.

Issue about final references in subscribers on GitHub:

https://github.com/ReactiveX/RxJava/issues/3148

NonConfigurationScope implementation:

https://github.com/partition/Dagger-Non-Configuration-Scope

Android’s StrictMode: https://developer.android.com/reference/android/os/StrictMode.html

javap, The Java Class File Disassembler

http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javap.html

Disclaimer

This is an updated version of my previous article about memory leaks in the first version of RxJava which covers changes introduced in RxJava2. It is also expanded with more insights and a small Kotlin example.