1

So I did tons of research regarding memory leaks in android and have read many StackOverflow posts. What I still don't understand is that according to Medium and StackOverflow posts (links mentioned below), If I pass my activity's context to an adaptor for Recyclerview and store it as a global variable in the adaptor, my activity along with the adaptor is still eligible for garbage collection. This is explained due to the fact that no GC roots have a reference to the adaptor or my activity (once it's destroyed) and so both will be garbage collected (Example1). However, why isn't the same case applied for inner classes? I get that they hold an implicit reference to their holding class, but if the activity (holding class in this case) gets destroyed and even if inner class is still executing some Async Task, shouldn't there be no GC roots pointing to any of them? (holding or the inner class) so both of them should get Garbage Collected? (Example 2).

Example1: (eligible for GC after activity gets destroyed):

public class MyAdaptor extends RecyclerView.Adaptor<>{

public Context context;

public MyAdaptor(Context context){
this.context=context;
}

}



public MyActivity extends AppCompatActivity {
private MyAdaptor myAdaptor;    

@Override
protected void onCreate(Bundle savedInstanceState) {
//some code
myAdaptor=new MyAdaptor(this);
}

}

Example 2: (not eligible for GC after activity gets destroyed):

public MyHoldingActivity extends AppCompatActivity {
private MyInnerClass myInnerClass;    

@Override
protected void onCreate(Bundle savedInstanceState) {
myInnerClass=new MyInnerClass();
}

class MyInnerClass{
//executing some long Async Task
}

}

Links for both of the post I mentioned are mentioned below: https://medium.com/@oznusem/circular-dependency-in-android-should-i-worry-about-it-814660ac79ec

Is it leak-safe to keep a Context/Activity instance in RecyclerView.Adapter?

Sorry If I didn't make my question clear, this is my first post on StackOverflow.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • Welcome to Stackoverflow! What do you mean by **global variable in the adapter**? Also, what exactly are you asking about **inner classes**? Your question isn't clear. Please explain. – David Wasser May 12 '20 at 21:00
  • I am really sorry for not being clear. By global variable I mean having a global field of type Context. For eg: Context context, and then setting it in the constructor(called from the activity) like: public MyAdaptor(){ this.context=context } Coming to the inner classes part, I read that they hold an **implicit reference** to the holding class. Therefore, if they are executing some Aynsc Task and the **activity(holding class)** gets destroyed, **Garbage Collector won't** clear the activity class from heap due to this reference. – Talha SIddiqui May 13 '20 at 00:13
  • But my confusion is that GC **should clear** it from heap since **no GC root object** is referring to **either** the outer class or the inner class? – Talha SIddiqui May 13 '20 at 00:17
  • You mean "member variable", not "global variable". When you refer to something as a "global variable", this means that the variable is globally available (ie: from any class) and therefore implies `static`. This is why I was confused. Don't use the term "global variable" unless you mean `static` variable. – David Wasser May 13 '20 at 10:48
  • Oh sorry, my bad. I 'll make sure to use the correct terminology next time. I have also edited my question for better clarification. – Talha SIddiqui May 13 '20 at 10:54
  • Don't use the term 'global variable' *at all* in Java, as they don't exist. @DavidWasser GC is defined by Java, not Android. – user207421 May 13 '20 at 11:23
  • @user207421 in Java, a `public static` variable declaration is a "global variable", as it is freely available for access from any class and any method and it does not belong to any object instance and will never be reclaimed by the garbage collector. This is pretty much the definition of a "global variable". – David Wasser May 13 '20 at 15:48

1 Answers1

0

Assuming that the AsyncTask has a reference to your inner class, then:

If an inner class of an Activity is running an AsyncTask, even if the Activity is finished, the GC will not be able to reclaim the Activity because the inner class has a reference to the Activity and the AsyncTask has a reference to the inner class and Android's internal thread manager has a reference to the AsyncTask. However, once the AsyncTask completes, the GC can reclaim the Activity since the internal thread manager will no longer have a reference to the AsyncTask.

This isn't really a memory leak, as it is only a temporary situation. The Activity will eventually be reclaimed. Real "memory leaks" occur when an object can never be reclaimed by the GC. Eventually such memory leaks can consume all available memory and cause a long-running application to crash with an OutOfMemoryException.

You can prevent this by cancelling your AsyncTask inside onDestroy() of the Activity.

Hopefully this answers your question.

David Wasser
  • 93,459
  • 16
  • 209
  • 274