question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Anonymous inner classes and memory leaks

See original GitHub issue

Hi,

I saw a lot of RxJava tutorials (for both 1.x and 2.x versions) on the internet where the Java anonymous inner classes (AIC) are used to construct Observable chains. Some guides (like this one) advise to use Retrolambda instead, but Retrolamda lambdas are converted to AIC anyway.

Now consider the following example:

public class SomeActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        getMyObservable()
        .map(new Function<InputType, OutputType>() {
            @Override
            public OutputType apply(final InputType input) {
                ...
                return someOutput;
            }
        })
        .subscribeOn(Schedulers.computation())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(new BiConsumer<ResultType, Throwable>() {
             @Override
             public void accept(@NonNull ResultType result, @NonNull Throwable throwable) throws Exception {
                // Handle result here...
             }
         });
    }
 }

Imagine MyObservable internally starts some long-running async task. As you can see, there are two AIC in the example above which hold implicit references to the enclosing Activity.

And what would happen on a configuration change:

  • A new Activity instance is created
  • The old Activity is not needed anymore and, therefore, it must be garbage-collected. But it can’t! Since the long-running task is still in progress the old Activity instance will be alive because of the implicit references that were passed to map and subscribe.

This popular tutorial suggests to overcome this issue by unsubscribing in onPause or some other lifecycle event. But, can you tell me, does unsubscribing really destroy somehow all these implicit references that were passed with AIC?

I’ve checked RxJava operators source code and, as far as I can see, there is no such functionality there (correct me if I’m wrong). For example, let’s take a closer look at ObservableFromCallable:

@Override
    public void subscribeActual(Observer<? super T> s) {
        DeferredScalarDisposable<T> d = new DeferredScalarDisposable<T>(s);
        s.onSubscribe(d);
        if (d.isDisposed()) {
            return;
        }
        T value;
        try {
            value = ObjectHelper.requireNonNull(callable.call(), "Callable returned null");
            ...

Here callable.call() may represent a long-running operation. And while this operation is executing the reference to the Observer s will be alive so it cannot be garbage-collected, right? Can you explain how exactly unsubscribing will kill the reference to the observer?

It seems that everyone is happy with AIC and Retrolambda and I feel like I’m missing something 😃

Issue Analytics

  • State:closed
  • Created 7 years ago
  • Comments:6 (3 by maintainers)

github_iconTop GitHub Comments

6reactions
JakeWhartoncommented, Feb 17, 2017

Stateless lambdas do not capture the enclosing instance even when Retrolambda is in use.

On Fri, Feb 17, 2017, 9:48 AM Stan Mots notifications@github.com wrote:

Thanks. So you are confirming that I can freely use AIC and there will be no memory leaks as long as I’m clearing CompositeDisposable in onPause?

What I don’t get is how exactly this “root” of the chain becomes broken? As I discussed above, in the case of ObservableFromCallable what would stop the callable.call() invocation taking into account that it is some long-running operation? Is clear() method from CompositeDisposable supposed to do that?

Here is how I’m analysing the code:

To create Observable we are using fromCallable https://github.com/ReactiveX/RxJava/blob/2.x/src/main/java/io/reactivex/Observable.java#L1582 (just for example purposes) 2.

As a supplier parameter we are passing our AIC 3.

This supplier is then passed https://github.com/ReactiveX/RxJava/blob/2.x/src/main/java/io/reactivex/Observable.java#L1584 to ObservableFromCallable 4.

The supplier (our AIC) is stored https://github.com/ReactiveX/RxJava/blob/d3455d0c9d57d522c31b5c25af83e8f2b8df12b6/src/main/java/io/reactivex/internal/operators/observable/ObservableFromCallable.java#L29 as an instance field of ObservableFromCallable 5.

When we are subscribing to the returned Observable there will be blocking invocation https://github.com/ReactiveX/RxJava/blob/d3455d0c9d57d522c31b5c25af83e8f2b8df12b6/src/main/java/io/reactivex/internal/operators/observable/ObservableFromCallable.java#L42. Since it is blocking the callable (which is our AIC in turn) will live as long as this operation is executing. And since AIC holds an implicit reference to our Activity it won’t be GCd too.

And here is the question: when CompositeDisposable is cleared will callable.call() be stopped somehow?

I’m sorry to bother you, I just wanna fully understand how rx operations work to prevent memory leaks.

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/ReactiveX/RxJava/issues/5107#issuecomment-280669390, or mute the thread https://github.com/notifications/unsubscribe-auth/AAEEEROlPwUv5sFQ0-p61MsRwf24hsyfks5rdbNFgaJpZM4MEP5I .

0reactions
stanmotscommented, Feb 18, 2017

Ok, thanks

Read more comments on GitHub >

github_iconTop Results From Across the Web

When exactly is it leak safe to use (anonymous) inner classes?
A memory leak will occur if an instance of an inner class survives longer than its outer class (an Activity ). -> In...
Read more >
Avoid memory leaks in inner classes - InfoWorld
This type of memory leak occurs because an inner class must at all times be able to access its outer class--which doesn't always...
Read more >
Dealing with Memory Leaks from Anonymous Classes in ...
In this post, I'm going to be looking specifically at memory leaks caused by anonymous classes. Anonymous classes are useful in android ...
Read more >
Catch Leak If You Can!. Memory leaks in Android are not so…
An example of memory leak with inner class ... This is the same situation as previous — an anonymous class can live only...
Read more >
Understanding Memory Leaks in Java - Baeldung
A Memory Leak is a situation where there are objects present in the heap that are no longer used, but the garbage collector...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found