A memory leak in Java happens when memory that the app no longer needs can’t be garbaged collected because some other code holds an unneeded reference. In an Android app, the most common memory leaks happen when a long running operation keeps a reference to an Activity’s context. Normally, when a user backs out of a screen, the memory used by the screen’s UI can be collected. But if a long running operation like a slow network call maintains a reference to the Activity – in a callback for example – the Activity and its UI can’t be garbage collected, creating a leak.
For the most part, this type of leak resolves itself, so they aren’t super noticeable to a developer. That is until you start seeing “random” OutOfMemory (OOM) crashes for your users. In a fast, Wifi connected development environment, the network call operations tend to end quickly. In the real world, though, a slow mobile network connection might stay open for 30 seconds or more. If an unlucky user just happens to navigate from screen to screen quickly while on a slow connection, the leaked Activity memory can lead to an OOM crash.
Detecting the Leak
Android Studio’s built in Memory Monitor is great for noticing when your app is leaking memory (or just using too much memory in general)
Here are a couple things you can try to find leaks:
Use a Home Baseline
One of the easiest ways to notice a memory leak to start your app is to check to set a baseline for the initial loaded state of your app. Then expore your app for a bit and press back to return home. Every time you return back to your app’s home screen, check to make sure the overall memory use does not increase.
- Start your app and wait for any loading to settle
- Press the garbage collect button and take note of how much memory is being used in your app’s “home” state.
- Navigate around the app
- Return back to home
- Quickly press the garbage collect button.
After step 5, the app’s memory usage should be about the same as your app’s home state (step 2). In the image, look at the yellow dashed line. The app is using the same amount of memory after the user returned to the home screen as the app was using when it first started. If you do this and your memory baseline is higher than when you started, you might have a leak.
Finding the Leak
Exporting to Memory Analyzer Tool (MAT)
Once you know there is a leak, it’s time to find out where.
Android Studio has a built in HPROF dump tool that lets you list all Java objects on the heap. Unfortunately, the built in HPROF tool is very limited.
I recommend using the standalone Memory Analyzer Tool (MAT)
One you’ve downloaded and installed MAT, you’re ready to start finding your leak. Caution: MAT might trigger flashbacks to the Eclipse days of Android development. :-)
To open a memory dump, in MAT, do the following:
- Get your app into the state where you suspect a leak
- In Android Studio, open Memory Monitor and press the Dump Java Heap button.
- Wait for the dump to finish. A list of objects will open in a new tab.
- In the Capture tab, find the dump you just created, right click and pick “Export to standard hprof”
Using the Memory Analyzer Tool (MAT)
MAT has a lot of tools to help you track down your leak. Each leak is different, so it will take some patience and exploration. Here is one strategy to get you started:
####1. Start by Filtering on “Activity”
Start by looking for too many copies of your Activity or Fragment classes. You can do this by switching to the Histogram view, and typing “Activity” in the search box. If you triggered a GC right before taking the HPROF dump, more than 1 copy of an Activity is suspicious.
In this simple example, we ended up with 3 copies of MainActivity. There should only be one instance, so it looks like we have a leak.
####2. Merge Shortest Path
You can easily use MAT’s “Merge Shortest Path to GC” feature to get a list of all the objects that are preventing an object in the histogram from being GC.
####3. Look for your classes
Once we have merged the paths, we can see 2 things keeping MainActivity in memory. The first item, ApplicationThread, is a part of the Android FW itself. It’s unlikely that Android’s own classes are causing a leak in your app. The second item however, the NetworkAPI library class, is one of my app’s own classes. I’m going to bet the problem is there.
Now that I know the NetworkAPI activity has the reference that’s causing the leak, I can head back to the code base and start fixing it!
If you’re passionate about dogs AND quality apps, we’re hiring!