Hunting down memory leaks in Android

One of the most common myths They told us about programming in a Garbage collector environment is there is no need to worry about memory management. Ok, that is usually right most of the time but it is not complete truth (in fact, memory leaks in GC environments are pretty difficult to spot). There are some potencial sources of memory leaks and some of them are not obvious and they can lead to a huge memory leakage.

A visual representation of an Android app with leaks

There is a well-known potential cause of memory leaks on Android which it is usually called “Leaking the Context” and it is a very problematic source of leaking memory because, as you know, an Activity (inherited from Context) is a potential god-object with a lot of references and it has to be destroyed and re-created every time you change the orientation of the screen.

Although it is a common issue is not usually well explained, or explained at all in Android tutorials or introductory books. There are some good links with amazing information about this issue but they usually lack of examples.

So I am going to take a “learning by example” approach to make you and extraordinary and merciless hunter of memory leaks.

Elmer Fudd, a pioneer of leak hunting and deeply misunderstood as almost all pioneers

The one you are going to see in every Memory leak example out there

This is the most common and easiest example of a memory leak you are going to find. First let´s take a look at this piece of code

public class MainActivity extends Activity
{
    static LeakMeNow leak; // static classes can be GC roots

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if(leak == null)
        {
            leak = new LeakMeNow();
            leak.logSomethingLeaky();
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu)
    {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    private class LeakMeNow
    {
        // This class holds a reference of Activity
        public void logSomethingLeaky()
        {
            Log.i("LeakExamples", "I know you are going to leak something here, just shake   your phone");
        }
   }
}

It looks pretty simple and unoffensive isn’t it? It is not so innocent, it hides a memory leak. LeakMeNow class is an inner class and all inner classes keep a reference of their enclosing classes. This leads to a memory leak if, as it is happening in this example, a static reference of this inner class has been declared. Fortunately this example is really simple to solve. We have two options.

Make the reference non-static:

LeakMeNow leak; // Non static class, safe now

If you really need a static reference and need access to variables from Activity, you have to make your inner class static and pass a WeakReference to your activity.

private static class LeakMeNow
{
    // It is not going to leak now
    private WeakReference upperClassReference;

    private LeakMeNow(MainActivity activity)
    {
        upperClassReference = new WeakReference(activity);
    }

    // This class holds a reference of Activity
    public void logSomethingLeaky()
    {
        Log.i("LeakExamples", "I know you are going to leak something here, just shake your  phone");
    }
}

Threads are dangerous too!

Threads are a potential cause of leaks too. Let’s see this code:

public class MainActivity extends Activity
{

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        new LeakyThread().start();

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) 
    {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    public class LeakyThread extends Thread
    {
        public void run()
        {
            synchronized(this)
            {
                try
                {
                    wait();
                } catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
            }
        }
    }
}

Every time onCreate is called a new thread is going to be created and started. When orientation is changed those threads will remain and, consequently the Activity is not available for collection.

Best solution is being careful with your thread lifecycle and stop them when necessary. In this particular example is really easy:

public class MainActivity extends Activity
{
    private LeakyThread thread;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);

       thread = new LeakyThread();
       thread.start();

    }

    @Override
    public void onDestroy()
    {
        super.onDestroy();
        thread.stopThread();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) 
    {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    public class LeakyThread extends Thread
    {
        public void run()
        {
            synchronized(this)
            {
                try
                {
                    wait();
                } catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
            }
        }

    public void stopThread()
    {
        synchronized(this)
        {
            notify();
        }
    }
  }
}

Basically you don’t have to create threads as anonymous classes and it is imperative to stop them before the destruction of the Activity.

A really subtle one (based on real events)

The third leak is going to be more difficult to spot. Although it involves a thread and We already know they are prone to problems, the thread involved here is not a subclass or an anonymous class of the Activity, it is completely separated of it. It means this thread should not be a GC root of the Activity (threads are potential candidates to be roots) but IT IS THE GC ROOT!!! Some code:

public class SemperVigilans 
{
	// This class, as techie new form of inquisition, it is going to check the incoming arrival of the end
	
	private ViewGroup viewGroup;
	private SemperVigilansThread thread;
	
	public SemperVigilans(ViewGroup viewGroup)
	{
		this.viewGroup = viewGroup;
		this.thread = new SemperVigilansThread();
		this.thread.start();
	}
	
	
	private class SemperVigilansThread extends Thread
	{
		
		public void run()
		{
			while(viewGroup.getWidth() != 666 && viewGroup.getHeight() != 666)
			{
				// World is save...at least for now
			}
			
			// Here a callback to warn the world about incoming apocalypse would be awesome
		}
	}

}

Our Activity:

public class MainActivity extends Activity
{
	private RelativeLayout layout;
	private SemperVigilans semperVigilans;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) 
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		LayoutInflater inflater = getLayoutInflater();
		layout = (RelativeLayout) inflater.inflate(R.layout.activity_main, null);
		semperVigilans = new SemperVigilans(layout);
	}
	
	@Override
	public void onDestroy()
	{
		super.onDestroy();
	}
 
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}
	
}

When this code is executed and orientation is changed you leak all the Activity but..Why? Well it is happening the same problem as before, but in a more subtle way. SemperVigilansThread is getting a reference from Activity through the Layout reference passed when SemperVigilans is created. Because Views objects have always a reference to the Context and in this case this reference reaches this thread. The solution is the same, control the lifecycle of your thread. If you remove all references to the Context but still your lifecycle is not controlled, you are still leaking but not the entire Activity.
View objects and Drawables objects are dangerous objects because both of them hold references to the Context. Drawables are even more dangerous because their references to their context are even less obvious Be careful with them.

Memory leaks are not exclusive of Activities

Leaking the context is probably the worse of all memory leaks you can get in Android but it is worth to mention that is not the only kind of leak you can get. As seen before, inner classes and anonymous classes are going to be naughty here:

public class LeakGenerator 
{
	// A leak generator
	
	public LeakGenerator()
	{
		
	}
	
	public Leak generateLeak()
	{
		return new Leak();
	}
	
	
	public class Leak
	{
		public Leak()
		{
			
		}
		
	}
}

MainActivity:

public class MainActivity extends Activity
{
	private Button firstTimeButton, everyTimeButton;
	private List<Leak> listOfLeaks;
	private LeakGenerator generator;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) 
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		firstTimeButton = (Button) findViewById(R.id.buttonfirsttime);
		everyTimeButton = (Button) findViewById(R.id.buttoneverytime);
		
		listOfLeaks = new ArrayList<Leak>();
		
		
		
		firstTimeButton.setOnClickListener(new View.OnClickListener()
		{
			@Override
			public void onClick(View v)
			{
				if(generator == null)
				{
				    generator = new LeakGenerator();
				}
				    
				listOfLeaks.add(generator.generateLeak());
			}
			
		});
		
		everyTimeButton.setOnClickListener(new View.OnClickListener()
		{
			@Override
			public void onClick(View v)
			{
				listOfLeaks.add(new LeakGenerator().generateLeak());
			}
			
		});
		
		
	}
	
	@Override
	public void onDestroy()
	{
		super.onDestroy();
	}
 
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}
	
}

This example is really easy, two buttons, one of them leaks one LeakGenerator() the first time you pressed and the other leaks a new LeakFactory() when pressed.

The first button only leaks the reference to LeakGenerator(). Even if the generator is not going to be use anymore it is going to live because every single leak reference has a reference to the generator. We can infer from this example that Objects from inner classes can’t outlive the outer class object which they hold a reference

The second button is a worse thing because every Leak object has a reference to a different generator.

That is all I have to say about this ugly issue I know you will face as an Android developer, it is better to be prepared before hunt them down.

Here some great essential links about Memory leaks:
Google I/O 2011: Memory management for Android Apps

Activities, Threads, & Memory Leaks

I hope you find useful this post. If you have some suggestions or you found some bugs just let me now 🙂

Happy Craft!

Advertisement

DroidTerm v2.0

Last information about the current state of DroidTerm. Please check it out

A month ago I released an Android app called DroidTerm to fulfill my needs of a simple serial terminal application. It worked pretty well but I started to want more features.
So I just released a new version of DroidTerm with a feature I really missed a lot. An Usb viewer to read Usb parameters in a meaningful way. Let me show you an example:

usbviewer1

In order to get a meaningful representation of vendor and product data DroidTerm fetches data from The USB ID Repository

I fixed some bugs too. So DroidTerm should be working better now If you have some problems using it just post a comment or email me.

Happy craft!