Reputation: 2004
In this case I am trying to understand some concepts related to memory leaks in Android. Though it is a frequent question, I am more interested on a real justification rather than a simple answer like "do not keep a reference to the activity".
These are the pages I've been reading so far:
link1: Garbage-Collection Roots—The Source of All Object Trees
link2: Memory leaks in Android
link3: Memory leaks in Android
and my conclusion so far is that the only way to have memory leaks with activities is when a component has a reference to an Activity and the lifecycle of this component is beyond the activity's lifecycle. The time you have a memory leak depends on the lifecycle of that component.
With that being said, I found this discussion with Jake Wharton related to Dagger 2 in an MVP architecture, where he wrote:
There is no leak. If the activity has outgoing references to dependencies and there are no incoming references to the activity it will be garbage collected.
As far as I know, if I have an activity which has a reference to a presenter which has a reference to a view interface (the implementation is the same activity), and then the activity is being recreated with a new presenter, then I do not see the reason why we can have memory leaks: the old activity and presenter are not longer reachable and thus they are potentially garbage collected. The only way I can have memory leak is if the presenter or any component inside the presenter has a lifecycle beyond the activity's lifecycle.
My questions are:
Am I missing something here?
Does having a reference to an activity mean I will have memory leaks? If that so, then does it mean the activity is considered a Garbage-Collection Root
Upvotes: 1
Views: 1261
Reputation: 2004
It took me time to get back to this question and get all the information possible to understand how this works and see if the statement "if you have an object referring to an Activity which has been replaced by another activity, then you have a memory leak" is true.
Firstly, I decided to run some code to see the memory's behaviour. I used a simple MVP project I have. The first thing I have done is to keep a reference to the activity in my presenter after the onDestroy method has been called:
public abstract class BasePresenter<T> {
//Activity or Fragment attached to Presenter
protected T view;
public void onViewCreated(T view) {
this.view = view;
}
public void onStart() {
}
public void onResume() {
}
public void onPause() {
}
public void onStop(){
}
public void onDestroyed() {
// view = null;
}
}
Then I turned on the "do not keep activities in memory" option and recreated the activity a couple of times.
When I dumped the memory I saw multiple instances of that activity, but after calling the garbage collector with the android memory profiler option they disappeared (as you know the GC is not called every time due to performance issues, just when it is needed).
So in this case the statement "if you have an object referring to an Activity which has been replaced by another activity, then you have a memory leak" is not true, or at least it is not complete to be true. In order to get a memory leak, that dependency graph needs to be referenced by a GC root.
Then I decided to uncomment that line in onDestroy:
public void onDestroyed() {
view = null;
}
and have a static reference to a view in my fragment (views have a reference to the activity where they are created)
private static View viewLeak;
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
progressBar = view.findViewById(R.id.progress);
if(viewLeak == null){
viewLeak = progressBar;
}
}
Again, I repeated the process of recreating the activity and in this case I had multiples instances of the same activity (two instances: one which was actively used and another one referenced by the static view). Of course I called the garbage collector but that extra instance of the activity stayed in memory. What changed here is that in this case we have a GC root which is the static variable:
Static variables are referenced by their classes. This fact makes them de facto GC roots. Source
Of course, there are multiple cases where you can get a CG root and thus, multiple cases where you can get memory leaks.
I hope this helps you to clarify some concepts related to memory leaks in Java as it did to me.
Other sources that were useful to understand this are: LINK1, LINK2, LINK3
NEW CONTENT: This is a video from google I/O talking about memory leaks. Please, pay attention when he says "long-live objects having a reference to an Activity" - minute 25:30.
Upvotes: 1
Reputation: 5617
Suppose you have an object A
that has a reference to Activity B
. Now suppose you finish the Activity B
. You can be sure that B
is ready to be garbage-collected.
Now suppose you manually call the GC. Will it recycle the Activity B
? No! Why? Because there's one object referring to it (object A
).
This is how a memory leak is born.
Upvotes: 0