technology

Dealing with Memory Leaks from Anonymous Classes in Android

As anyone who has programmed in C can tell you, memory management can be a challenging task. In Java it is significantly easier- so much easier that it lulls many developers into a false sense of security. You can still leak memory in Java, and leaking memory leads to crashes and bugs that will sink your app.

In this post, I’m going to be looking specifically at memory leaks caused by anonymous classes. Anonymous classes are useful in android programming as a way to avoid writing extra boilerplate. To quote the Oracle docs, they “enable you to make your code more concise. They enable you to declare and instantiate a class at the same time. They are like local classes except that they do not have a name. Use them if you need to use a local class only once.”

This is all well and good until they last longer than the context around them. Consider the following, rather benign looking code:

This code may leak; If the activity is destroyed before the thread is finished executing, the activity cannot be garbage collected. See the following LeakCanary leak report:
5d8c947a-ce82-11e6-8642-17208c3bfec9

Creating a thread in this way makes it an anonymous inner class, and thus has access to the outer class’s variables. To maintain this access, it needs a reference to the outer class, or in this case the activity. Thus, if thread is still active when the activity would normally be removed from memory, the inner class tells the garbage collector to stop and prevents the activity from ever being cleaned up. This is particularly problematic because activities tend to take up a large amount of memory with things such as view hierarchies.

Other asynchronous tasks are also culprits of leaks for similar reasons; Handlers, AsyncTasks and more keep references to the outer class that spawned it after it has been through the end of its lifecycle, preventing it from ever being garbage collected.

So how do we use these classes without hosing our memory usage and causing an eventual OutOfMemoryError? First, we have to de-anonymize them and make them static. This is the same code, but using static inner classes instead:


As you might have guessed from reading the class name, this still leaks:
61c06eae-ce82-11e6-8073-74ddedd5e469

Our inner class needs to hold references to the activity and the textview so it can change the text as needed, but holding these references causes the activity to leak if it’s destroyed while the thread is still running and holding its references. To fix this, we need to use weak references. Weak References are references that we tell the garbage collector we’re not particularly worried about holding on to. If an object is being garbage collected and is only referenced weakly, we let it go. For our purposes this is just what we want; Instead of passing the context as a strong reference to the inner class, we pass it as a weak reference. Then, when we go to use it within the inner class, we simply check if it’s null (and thus was garbage collected) before we use it. In this way, we allow the activity to be released if it hits the destruction part of its lifecycle before our inner class has been executed and cleaned up. This is the same code as earlier, but without memory leaks:

Now our app can happily move on without clogging memory with old, unused references, and be sure that the things it wants to forget remain forgotten. While learning about this, these two blogs were invaluable resources; I highly suggest you check them out if you’re interested in learning more:

http://blog.nimbledroid.com/2016/05/23/memory-leaks.html
http://blog.nimbledroid.com/2016/09/06/stop-memory-leaks.html
http://www.androiddesignpatterns.com/2013/01/inner-class-handler-memory-leak.html

Discussion

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s