Table of Contents:
- Introduction
- What is a Memory Leak?
- How Memory Leaks Occur
- How to Avoid Memory Leaks
- Finding Memory Leaks
- Other Important Stuff
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.
-
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. -
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
Create an Account
Similar Posts
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
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
Comments