Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is the reference to the Context a memory leak?

According to Romain Guy this kind of code is prone to memory leak due to the fact that

.... views have a reference to the entire activity and therefore to anything your activity is holding onto; usually the entire View hierarchy and all its resources.

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

  TextView label = new TextView(this);
  label.setText("Leaks are bad");

  setContentView(label);
}  

I am not clear about this.
Assuming an application with 1 activity, this is the longest lived object and can be recreated as needed. Which means that all of its instance fields (which can and usually are Views) can be null at any time.
And any static instance field will live for the same duration as the activity itself.
So how can we get a memory leak with code like the above or the following:

private static Drawable sBackground;

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

  TextView label = new TextView(this);
  label.setText("Leaks are bad");

  if (sBackground == null) {
    sBackground = getDrawable(R.drawable.large_bitmap);
  }
  label.setBackgroundDrawable(sBackground);

  setContentView(label);
}
like image 336
Jim Avatar asked Dec 24 '22 21:12

Jim


1 Answers

Assuming an application with 1 activity, this is the longest lived object

No, it is not. There are other objects in your process (e.g., Application, content providers) that will outlive an activity instance.

In particular, note that activities get destroyed and recreated by default on a configuration change (e.g., screen rotation).

And any static instance field will live for the same duration as the activity itself.

No. Static fields are around as long as the process is around. Your activity instances can be shorter-lived than that.

So how can we get a memory leak with code like the above or the following:

There is no static field in your first example.

Romain Guy explains the second scenario in the blog post that you linked to:

This code is very fast and also very wrong; it leaks the first activity created upon the first screen orientation change. When a Drawable is attached to a view, the view is set as a callback on the drawable. In the code snippet above, this means the drawable has a reference to the TextView which itself has a reference to the activity (the Context) which in turns has references to pretty much anything (depending on your code.)

And, if you added LeakCanary to your project, you would see the leak.

So, let's walk through this:

  • User taps on the home screen launcher icon for your app, which is tied to this activity
  • Your process is started
  • Your activity instance is created, and is then called with onCreate()
  • sBackground is null, and so you assign it the getDrawable() result
  • Your activity UI appears on the screen
  • The user sneezes and accidentally rotates the screen of the device as part of reacting to the sneeze
  • Your old activity instance is destroyed
  • A new activity instance is created, and is then called with onCreate()
  • sBackground is not null, and so you leave sBackground alone

And you have your leak. As Romain explained, sBackground has a not-very-obvious reference back to your original activity instance. So, now you have two outstanding instances of this activity: the leaked original, plus the new one created due to the configuration change.

like image 67
CommonsWare Avatar answered Dec 26 '22 12:12

CommonsWare