Let's explore an example of what the memory leak can look like. For this, we will create MockActivity with this content:
public class MockActivity extends AppCompatActivity {
private String[] bigArray = new String[10000000];
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mock);
bigArray[0] = "test";
Log.i("APP", "Activity Created");
}
@Override
protected void onDestroy() {
Log.i("APP", "Activity Destroyed");
super.onDestroy();
}
}
We will start this Activity from MainActivity by adding a button for testing purposes in its layout:
<Button
android:id="@+id/start_another_activity_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/hello_world_salute"
android:layout_margin="16dp"
android:text="Start!" />
Here, the action will be handled by the following in the MainActivity again:
@OnClick(R.id.start_another_activity_button)
public void onStartAnotherActivityButtonClick(Button button) {
startActivity(new Intent(this, MockActivity.class));
}
Also, do not forget to register this in AndroidManifest.xml:
<activity android:name=".MockActivity" />
This Activity, when it gets created, will allocate a big array (around 40MB) in the memory. Using Monitors in Android Studio, we can see how that looks:

In the figure, we can see that memory usage jumped to around 68 MB after the MockActivity was started, and later when it was killed and we pressed Initiate GC button, it returned to the normal levels.
However, now let's add a reference to the MockActivity from some global, application scope variable. We can do this by simply introducing a static variable in the MockActivity class and make it reference instances of it, like this:
public class MockActivity extends AppCompatActivity {
private static final List<MockActivity>
INSTANCES = new ArrayList<>();
private String[] bigArray = new String[10000000];
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mock);
bigArray[0] = "test";
INSTANCES.add(this);
Log.i("APP", "Activity Created");
}
@Override
protected void onDestroy() {
Log.i("APP", "Activity Destroyed");
super.onDestroy();
}
}
Now, let's try starting this Activity and quitting it (by pressing Back) several times. The Memory Monitor will look like this:

In the preceding figure, we can see that memory usage keeps growing, and it doesn't even drop if we initiate Garbage Collection. This happens because the class of MockActivity holds a reference to all the instances of MockActivity that were ever created. We can even see in the logs that the Activity is actually destroyed:
APP: Activity Destroyed
However, the instance itself isn't deleted from the memory. If we try starting MockActivity a few more times, it will fail with the following exception:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: packt.reactivestocks, PID: 900
java.lang.OutOfMemoryError: Failed to allocate a 40000012 byte allocation with 33554336 free bytes and 36MB until OOM
at packt.reactivestocks.MockActivity.<init>(MockActivity.java:14)
at java.lang.reflect.Constructor.newInstance(Native Method)
at java.lang.Class.newInstance(Class.java:1572)
This was a very straightforward example, but it can sometimes be more subtle. Consider this code for MockActivity:
public class MockActivity extends AppCompatActivity {
private String[] bigArray = new String[10000000];
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mock);
bigArray[0] = "test";
getApplication().registerComponentCallbacks(new
ComponentCallbacks() {
@Override
public void onConfigurationChanged(Configuration newConfig)
{
Log.i("APP", "Some logging");
}
@Override
public void onLowMemory() {}
});
Log.i("APP", "Activity Created");
}
}
The same will happen as before--the Activity will leak and, at some point, an OutOfMemory Exception will be thrown. Why does this happen?
The application (accessed by getApplication()) is a global object that gets created once per application and gets destroyed only when the application is killed. In the application, we've used the registerComponentCallbacks() call to register our callback from inside the Activity. However, the callback that we've created with the following is an anonymous inner class that has an implicit this reference to the MockActivity:
new ComponentCallbacks() {...}
Thus, MockActivity instances are always referenced by the Application and can never be garbage-collected.