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!

Advertisements

Get vendor and product name from VID and PID in Android

Usb devices are identified by two 16-bit numbers known as VID and PID (Vendor id and Product Id). These keys are, among a lot of other things, an advantage of Usb over other vintage ports as serial ports. Unfortunately if you are developing an application which needs to show a readable information about devices attached to usb root hubs these keys are pretty cryptic and need to be decoded into human readable information.
During the development of DroidTerm I reach this problem because I am working on a Usb viewer of what is attached to Usb port. First I was looking for a beautiful REST API with its fancy methods and its json responses but I only could find The Usb Id repository which is a text file and nothing more. But It seems very complete and reasonably updated so there is no reason to moan.
I defined this features:
– Data should be stored locally using SQLite to avoid dependency on network.
– Local database should be updated when new devices were added to Usb Id Repository.
– All database operations should be encapsulated.
So I released this piece of code to fill my own requirements.
An example of how it works:

/*
* Example of use
* @author: felhr (felhr85@gmail.com)
*

// Clone all the files needed: git clone https://gist.github.com/afe18397dc2441862337.git
// Permissions needed: <uses-permission android:name="android.permission.INTERNET" /> 

// There are some callbacks related with created, opened and updated database events. It is not necessary to use them.
private UsbDataProvider.UsbDbCallback mCallback = new UsbDataProvider.UsbDbCallback()
{
	@Override
	public void onDbOpenedFirstTime(boolean status)
	{
	// status == false means database could not be created due to an error fetching data from source
	// status == true means database was successfully created

	// Code here
    }

	@Override
	public void onDbOpened()
	{
	// Database opened
	// Code here
	}

	@Override
	public void onDbUpdated(String newVersion)
	{
	// Database updated with newVersion
	// Code here
	}
};

UsbDataProvider dataProvider;
dataProvider = new UsbDataProvider(context, mCallback); // Create and open, open or update and open database if necessary. Notifications on callback
//dataProvider = new UsbDataProvider(context)

String vid = "03f0"; // Must be an hex representation of 16 bit number (0000-FFFF). Don't worry about uppercase or lowercase
String pid= "010C"; // Must be an hex representation of 16 bit number (0000-FFFF). Don't worry about uppercase or lowercase

UsbData data = dataProvider.lookup(vid, pid); // Returns null if vid or pid are not valid inputs or database could not be created
if(data != null)
{
	String vendorName = data.getVendorName(); // Vendor name
	String productName = data.getProductName(); // Product name
}

I hope you find this piece of code useful. Happy craft!