Card image cap

Table of Contents:



Prefer to watch video instead of read? Watch here.


Introduction


In this blog post I'm going to talk about memory leaks on Android.

First I'll talk about what they are, why you want to avoid them, and the most common ways they occur. This will include some practical examples (of course).

Then I'll talk about how to avoid them, how to find them in your code (using a handy library), and some cool tricks that I've learned.


What is a Memory Leak?


Every Android device has a fixed amount of memory. And every process on the device requires memory to operate. That includes your applications.

This fixed amount of memory is known as the heap. So at any given time, the amount of freely available memory will be in the heap.



I like to think of the heap as a large structure of memory blocks. Note that in Fig. 1 all the memory blocks are the same size. But in reality the memory blocks can be any size. It will depend on the object that the memory block is assigned to.



When an object is created, memory is removed from the heap.

To prevent the android system from getting overwhelmed under heavy loads, some objects are automatically cleaned up by something know as the garbage collector, or GC for short.



The GC has a built-in algorithm for determining which objects to clean up and effectively destroy. When the GC "cleans up" an object, the memory that the object occupied is returned to the heap. To understand memory leaks, you must understand the GC.



A memory leak occurs when the GC is unable to clean up an object or a set of objects. The result is essentially a lost chunk of memory that can not be regained. Obviously this is not a good thing as memory is a finite resource.


How Memory Leaks Occur


By definition, a memory leak will occur when memory is allocated (example: an object is created), but that object is never freed (example: the object is never released from memory). There's several ways this can happen. The most common way by far is saving a reference to a view or a context inside a background thread.

Consider the following example:


The activity contains a single view, a TextView. Inside the activity is an inner class named MyAsyncTask that extends AsyncTask. Notice the MyAsyncTask class is saving a reference to the TextView. Then inside the onPostExecute method, some text is being set to the TextView. This is something you should never do.

You should never do this because if for some reason the MyAsyncTask is still running when the activity is destroyed, the garbage collector will not be able to free up the resources used by the activity. It can't free up the resources because a reference to the activity is saved on another thread - the thread that the doInBackground method runs on.

Keep in mind the same goes for a Context object. If you save a reference to the Context or an Activity, you run the same risks.


How to Avoid Memory Leaks


The way I see it, there's 2 practical things you can do to avoid memory leaks. Keep in mind the main goal here is to avoid saving a reference to a Context, Activity, or View object inside a background thread.

  1. Declare your inner classes as static.

    If you do this, the compiler in Android Studio will give you a warning. This is my preferred method. It removes the possibility of human error (and since we're all humans we all make mistakes). The compiler will give you a warning, and you can remove the reference.


  2. Use a WeakReference.

    This is what I would consider a "last resort". This is what you should do if you absolutely must save a Context, Activity, or View reference on a background thread.


    Using a WeakReference essentially "marks" the object for garbage collection. If it's a WeakReference, the GC is able to free the resources.


Finding Memory Leaks


Read my blog post on Finding Memory Leaks with Leak Canary.


Other Important Stuff


GC events can cause your application to lag. For example if you have a large number of GC events happening all at the same time, chances are, your app UI will lag. So the question becomes, how can you avoid allocating a large number of objects in a short period of time? When does that happen?

One example is declaring new objects inside a nested loop. Every time the loop runs, a new object is declared and therefore a new block of memory is removed from the heap. Eventually the GC will have to step in a clean up the mess you've made.
for(int i = 0; i < 100; i++){
    for(int j = 0; j < 100; j++){
        int m = j;
    }
}


Nested loops are usually a good thing to avoid when possible, but if you can't, try doing something like this instead. Declare a single object outside the loop. Then assign it inside the loop.
int m = 0;
for(int i = 0; i < 100; i++){
    for(int j = 0; j < 100; j++){
        m = j;
    }
}


Staying in the Loop


If you want to stay in the loop and get an email when I write new blog posts, Follow me on Instagram or join the CodingWithMitch community on my website. It only takes about 30 seconds to register.





Authors


Mitch Tabian

codingwithmitch.com

Software Dev


Create an Account



Have an account? Log In

CodingWithMitch Members

Unlimited access to all courses and videos

Step by step guides to build real projects

Video downloads for offline viewing

Members can vote on what kind of content they want to see

Access to a private chat with other communnity members & Mitch

Become a Member

CodingWithMitch Members

Unlimited access to all courses and videos

Step by step guides to build real projects

Video downloads for offline viewing

Members can vote on what kind of content they want to see

Access to a private chat with other communnity members & Mitch

Become a Member

Comments