Skip to content

Anonymous inner classes and memory leaks #5107

@stanmots

Description

@stanmots

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions