So that you can start on Memory Leaks in Android Apps

Gaurav Goyal
AndroidPub
Published in
9 min readDec 2, 2017

Why am I here

Purpose of this article is to provide an introduction of Memory leaks and how to find and avoid some of the obvious ones.

As a programmer you would want to write a code which makes you and other programmers smile and proud. If you write a program with some obvious leaks, you might see some of your code rewritten by others. Personally that’s not something I would like.

What are you talking about

As per Google, Memory Leak is “a failure in a program to release discarded memory, causing impaired performance or failure”. In other words if you have an object lying in heap memory and is of no use, that will eat up space from your app memory. As you keep using the app , the space will be utilised for further allocation until there is no more memory left and then comes every developer’s nightmare, your app will slow down, there would be lagging and crashes.

Wait, Why does every one keep talking about Garbage collector then, Aren’t it supposed to do just that?

Yes, It does what it is meant for, It will clear out the objects which does not have any references in memory. I repeat, GC takes all the heap objects, iterate them one by one to find out the objects which does not have references in memory, if not it will release the memory, if yes object will still reside in memory.

Yeah, that sounds perfect. If an object is not being used, GC will collect it. Again, why do we have memory leaks?

There can be instances when unknowingly or knowingly you are holding reference of an object. Let’s just say you have an object B but static one which has Object A as its attribute. now since B is static it will always be in the memory along with A, A can’t be cleared by GC because B holds reference of A.

Ah, I get a little idea, Can you explain more. One Question, Doesn’t GC slow down the app when it runs to clean up the heap memory? 😎

No, not really. GC runs in another thread so usually it won’t have any impact on the application’s performance. However if your app is running out of memory, this might hang up your app and cause some serious issues.

Ok, Now the explaining Part

Let’s try out few of the common mistakes which we developers do.

1. Static Objects

public class StaticClassWithActivityReference {    private static StaticClassWithActivityReference a;
private Context context;
private StaticClassWithActivityReference(Context context){
this.context = context;
}
public static StaticClassWithActivityReference getInstance(Context context){
if(a==null)
a = new StaticClassWithActivityReference(context);
return a;
}
}
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val leakObject = StaticClassWithActivityReference.getInstance(this)
}
}

Above Code has

  1. A Singleton class named StaticClassWithActivityReference which takes context as parameter to create a singleton object.
  2. An Activity class named MainActivity, which is getting an instance of StaticClassWithActivityReference.

What happens to an activity if user rotates the device? Activity (Calling it old Activity further) gets destroyed and recreated again. Logically, Old Activity should be removed from Heap. But It can’t be released in our case since leakObject is a static object and It has a reference of Activity so it will remain in memory forever. Which is why old activity would not be destroyed and will result into a Memory Leak. Think about a case when a user continuously rotate device from Portrait to landscape mode and vice-versa n times, there would be 2n activity instances lying in the heap memory.

We will be discussing on how to solve this problem in the later part of this Article.

😢 I get it now, But I can miss such small things while coding, How do I know if I have such stupid leaks in my code.

Android Studio comes with Android Profiler and Android Device monitor in it. Under Profiler you can find memory allocation tracker which helps to find out memory usage and generates a graph based upon allocated memory to objects at that point of time. If you see a sudden spike or monotonous increment in it, You might want to check out for Leaks.

Android Profiler

Under Android Device Monitor, you can find an option of Dump HPROF which will generate heap dump of the current process. Using HPROF file you can find Leak Objects and references which caused them.

Hmm, Do I have to do this every single time I write a piece of code, This is a tedious task. Any work around?

Yes, there is. You can add Leak Canary to your project, it will help you detect memory leaks on fly also It’s very easy to integrate and use.

Once you add the library to your project, It will keep notifying you about the leaks as you run through your Application. For this example it will show something like this.

Tadaaa.. This would work for someone as lazy as me. Now that I know what is a memory leak, tell me more about so called obvious leaks 😝

2. Inner classes :

public class InnerClassActivity extends AppCompatActivity {    @Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new MyAsyncTask((TextView) findViewById(R.id.text_view)).execute();
} private class MyAsyncTask extends AsyncTask<Void, Void, String> { private final TextView textView; public MyAsyncTask(TextView textView) {
this.textView = textView;
}
@Override
protected String doInBackground(Void... params) {
try {Thread.sleep(10000);} catch (InterruptedException e) {}
return "some text";
}
@Override
protected void onPostExecute(String result) {
if (textView != null)
textView.setText(result);
}
}
}

Above Code has

  1. An Activity named InnerClassActivity
  2. An Async task named MyAsyncTask

The above written code will result into memory leak for two reasons:

MyAsyncTask is an inner class, hence it carries reference of the activity.

Whenever there is a task which can go out of activity/fragment lifecycle, there are chances of memory leaks if it carries reference of component which started it. To Avoid such memory leaks we can make MyAsyncTask a Static class. A static inner class does not have reference of outer class.

MyAsyncTask has TextView which belongs to Activity.

Leak Detail Screen By Leak Canary

Here TextView will have reference of the activity and MyAsyncTask will have reference to TextView, which won’t allow activity to go off from heap unless the thread is destroyed. To Avoid such issue, We can try using Weak References.

Sorry, Weak Reference ?

As name suggests, A Reference which is not strong enough to keep the object in memory. When GC tries to release memory, it looks for Hard References ( By default every declared object is Hard Referenced) so an object with no hard references will be released from Heap even if it has weak references associated with it.

Read more about Weak References:

Okay, I get it now. So finally how would MyAsyncTask look?

public class InnerClassNoLeakActivity extends AppCompatActivity {    @Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new MyAsyncTask((TextView) findViewById(R.id.text_view)).execute();
Intent i = new Intent(this, MainActivity.class);
} private static class MyAsyncTask extends AsyncTask<Void, Void, String> { private final WeakReference<TextView> textViewWeakReference; public MyAsyncTask(TextView textView) {
this.textViewWeakReference = new WeakReference<>(textView);
}
@Override
protected void onCancelled() {
}
@Override
protected String doInBackground(Void... params) {
try {Thread.sleep(10000);} catch (InterruptedException e) {}
return "some text";
}
@Override
protected void onPostExecute(String result) {
TextView textView = textViewWeakReference.get();
if (textView != null)
textView.setText(result);
}
}
}

Wonderful, Next Please !

3. Anonymous class

We all use anonymous classes in Callbacks, listeners, threading etc. However when you create an anonymous class, it keeps reference of original class. A code as simple as this can cause memory leaks.

new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(100000);
Log.d(this.getClass().getSimpleName(), "Thread finished");
} catch (InterruptedException e) {}
}
}).start();

So you should try avoiding anonymous classes for long running tasks or if there is possibility that they won’t match with Corresponding component life cycle.

Solution would be same as Inner classes.( In this case make the class Static so that it does not carry reference of original class)

4. Wrong Context

Context, How many times and how many places we all have used it, whatever we want to do whether it is accessing resources or system services or we want to interact with another application component, context is something we can rely upon heavily But there are so many of them like Application Context, Activity Context, Base Context etc., which one to use where? It is important to use right context at right time.

Going back to out Static Objects example, if we do this

a = new StaticClassWithActivityReference(context.getApplicationContext());

instead of

a = new StaticClassWithActivityReference(context);

We would not face memory Leak, On Device rotation old activity gets destroyed and recreated again. Also, Since static object is having reference of old activity, It would still reside in memory but same does not happen if we use application Context, Application will always be single instance running hence does not matter what you do with your application, there can never be duplicate instance of the Application and so no memory leak.

Context is an abstract class which is implemented by Application, Activity and many others. It’s important to understand different types of context and their uses, you can refer this and this to read more about Context.

5. Unregistered Listeners

Events, Locations updates etc. they all have their register and unregister listeners. If you register a service but forget to unregister, it might lead to memory leaks.

//register
locationManager
.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER,
MIN_TIME_BW_UPDATES,
MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
//unregister
locationManager
.removeUpdates(TrackGPS.this);

When you register for events there are certain resources which get allocated memory and once you stop using the service, service should be able to release memory for those resources. To do that, it is important to call unregister listener most of the time.

Got it, I think i can start killing these memory leaks now. 😈

This Article gives you basic idea of memory leaks, If you want to explore more. Here is few other references.

YouTube , YouTube, Android Developers Blog

Conclusion

Recently from my personal experience I came to realise that there are things in an application which might not impact user visually and immediately but had to be taken care of for a smooth and flawless experience. Memory usage is one of them, Memory usage is equally important as other UI or Network related stuff. Initially it looks like a boring and difficult task but once you understand to fix them, you would want to scan all of your code and fix them all.

Here is something extra for you to try (UI block detection Library)

Also you can refer Git Repository for the mentioned examples.

Lastly, Thank you for reading the Article, Any Questions and suggestions are most welcome, Also if you have got anything out of it, Please clap and follow. See you soon. Happy Coding 😃

--

--