Practical Guide To Solve OutOfMemoryError in Android Application
OutOfMemoryError or simply OOM is something that every Android developer must have encountered with. If you haven’t seen any OOM in your Android application, then you are going to have one in future. The OutOfMemoryError comes in Android due to memory leaks. So, in order to remove the OutOfMemoryError, you need to remove memory leaks from your Android application.
In this blog, we will solve the OutOfMemoryError in Android with the help of some practical examples. So, let’s get started with the basics of Memory Leak in Android.
What is a Memory Leak?
When you run some application on the Android device, then the Android system will provide some memory to your application for its working. All the variable creation, function creation, activity creation, etc takes place in that memory only.
For example , if the Android System allocates 100MB for your application, then your application can use 100MB max at one particular time. With due course of time, if space allocated to the application gets reduced or very few amounts of space is left, then the Garbage Collector(GC) will release the memory that is being held by those variables and activities that are of no use. In this way, the application will again gain some space.
NOTE: The Garbage Collector performs the task of releasing the memory automatically and you don’t have to do anything for this.
But sometimes, when you write the code in a bad manner such that your code holds the references of objects that are not required anymore, then in this situation, the Garbage Collector is unable to release the unused space and no space will be left for the application for its further working. This is called a Memory Leak .
Due to the phenomenon of Memory Leak in Android, we encounter with the OutOfMemoryError in Android because your code is holding the references of the objects that are not required anymore and the Garbage Collector is unable to perform its job and your application uses all the space allocated to it by the Android System and is demanding for more.
What may be the reasons for OutOfMemoryError?
There are various reasons that can lead to OutOfMemoryError in Android. Some of the common reason for Memory Leaks that results in OutOfMemoryError are:
- Use of Static view/context/activity
- Register and unregister listeners
- Non-Static inner class
- Wrong use of getContext() and getApplicationContext()
Let’s learn one by one in detail.
Use of static views/context/activity
If you are using some static views/context/activity then you are going to have OutOfMemoryError(if your activities are dealing with lots of space). This is because the view or the context or the activity will be held by the application until the application is alive and due to this the memory taken by these will not be released by the Garbage Collector.
For example , In your application, you are having 4 activities i.e. A , B , C , and D . Activity » A » is your main activity and you can open the activity B , C , and D from A . Now, suppose the activities B , C , and D are holding static reference to its context and each activity is using 2MB and total memory allocated to the application by the Android System is 4MB. So, when activity » B » will be started then the memory used by the application will be 2MB. Now, come back to the activity » A » and start activity » C «. Now, the memory used by the application will be 4MB(2MB for Activity B and 2MB for Activity C ). Again come back to activity » A » and start activity » D «. Now, your app will require 6MB(2MB for B , 2MB for C and 2MB for D ), but the allocated memory is 4MB. So, here while opening the activity » D «, you will get OutOfMemoryError because the allotted memory is 4MB and you are demanding for 6MB.
Let’s look at one practical implementation. Here in this example, I am using Bitmap. By using more than 5 images of size 1MB each, my application will encounter OutOfMemoryError on my mobile device. In your case, the number of images that can result in OutOfMemoryError can be more than or less than 5. You can find the limit by adding images to the BitmapArray one by one and whenever you get the OOM, that is the limit of your device.
So, my application is having one MainActivity and from this activity, I can go to » ActivityB » and » ActivityC «. Both these activities i.e. » ActivityB » and » ActivityC » are having static reference of their context. So, space used by these activities will not be deleted by Garbage Collector.
Following is the code for ActivityB :
Following is the code for the ActivityC :
So, if you launch the ActivityB from MainActivity , then there will no OutOfMemoryError because the limit for my mobile device is 5 images and in the ActivityB , I am using only 3 images.
Now, come back to the MainActivity and launch ActivityC and you will get OutOfMemoryError because here in the ActivityC , we are using 3 images and since we are using the static context in both ActivityB and ActivityC , the reference of ActivityB is still with us and overall we have 6 images(3 from ActivityB and 3 from ActivityC ) and the limit for my mobile device is 5 images. So, OutOfMemoryError will occur.
To remove this error, make the context or view or activity, Non-Static.
Register and unregister listeners
In Android, we use various kind of listeners to listen to the changes like the location change. So, never forgot to unregister your listener when you are done using that listener. If you are not unregistering the listeners, then the listener will be present in the background and the space taken by the listener will not be deleted by the Garbage Collector even when the listener is of no use. So, it is better to unregister the listener if not in use. Let’s look at one practical example.
In this example, we are having one Singleton class named » SomeListener » that will be having two functions i.e. register() and unregister() . The register() function is used to add the activity in an activity array and the unregister() function is used to remove the activity from the activity array. Following is the code for the Singleton class:
In my mobile device, if I am adding more than 5 images, then the application will give OutOfMemoryError. So, in this example, we will be having three activities i.e. MainActivity , ActivityB , and ActivityC . The ActivityB and ActivityC will be called from MainActivity and in the onCreate() function of both these activities, we will call the register() method of the Singleton class to add the activities in the activity array.
Following is the code of ActivityB :
Following is the code of ActivityC :
If we call the ActivityB from MainActivity , then the reference of 3 images will be stored(as we are storing in the Singleton class). Here, no OutOfMemoryError will be shown because our limit is of 5 images. Now, if we come back to the MainActivity and call the ActivityC , then we will get OutOfMemoryError because initially, we are having reference to 3 images and when ActivityC is launched, then 3 more images came and overall we have reference to 6 images(as we are using the same Singleton class) and our limit is of 5 images. So, we will encounter MemoryOutOfBoundError.
To remove this error, you have to deregister the listener when the activity is closed or of no use. So, add the below lines of code to unregister the listener when the activity is stopped:
The nested class must be Static:
If you are having some nested class in the application, then make it a static class because the static class does not need the outer class implicit reference. So, if you are using the inner class as non-static then it makes the outer class alive until the application is alive. So, this can lead to OutOfMemoryError if your class is using a lot of memory. So, it is better to make the inner class Static.
In Java, you have to make the inner class static by your own but in Kotlin, the inner class is by default static in nature. So, you need not worry about static inner class in Kotlin.
Wrong use of getContext() and getApplicationContext() in third party libraries
We use many third-party libraries in our application and most of them use Singleton class. So, if you are passing some context to a third-party library and that third-party library is beyond the scope of the current activity, then use getApplicationContext() and not getContext() .
Generally, we do the below thing in our application:
Here, initialize is some static function in that library and it uses the context something like this:
But some of the libraries don’t use the above notation. So, in that case, it will use the context of the current activity and due to this, the reference of the current activity will be held until the application is alive and this can result in OutOfMemoryError(as the initialize function is static).
So, it is better to use the getApplicationContext() in your code explicitly and don’t depend on the third-party libraries to do that.
These are some of the techniques that can be used to remove OutOfMemoryError from our application. It is better to write the code that doesn’t give any OutOfMemoryError. Still, if your project is very big and you are getting it harder to find the class that is responsible for OutOfMemoryError, then you can use the memory profiler of Android Studio to find the classes responsible for OutOfMemoryError.
Also, there is one library called LeakCanary , that tells you about the class that is responsible for memory leaks in your Android application(if any).
In this blog, we learned how to remove OutOfMemoryError in Android. This can be removed by using a non-static reference of views/context/activity, by registering and unregistering the listeners, by using a static inner class. So, in future, if you are writing some code in Android, then I am sure that you are going to use these to avoid OutOfMemoryError.
That’s it for this blog. Hope you learned something new today.
Do share this blog with your fellow developers to spread the knowledge. You can read more blogs on Android on our blogging website .
Fixing Memory Leaks in Android – OutOfMemoryError
By Rebecca Franks
Memory leaks in Android are quite easy to create. The unsuspecting developer might be making a few memory leaks every days without realising. You probably haven’t noticed them yet or even know that they exist. Until you see an exception like this….
What? What does that even mean? Does it mean my bitmap is too large for the Android System?
Unfortunately, seeing this kind of Stack trace may be a bit deceiving. When you receive an OutOfMemoryError, it generally means, 9 times out of 10 that you have a memory leak. My first impressions of this stack trace left me confused and thinking that my bitmap was too large… Boy was I wrong.
What is a memory leak?
A failure in a program to release discarded memory, causing impaired performance or failure.
How is a memory leak caused in Android?
Memory leaks in Android are actually quite easy to make, which is probably part of the problem. The biggest issue is the Android Context object.
Every app has a global application context ( getApplicationContext() ). Every activity is a subclass of Context , which stores information related to the current activity. More often than not, your memory leak will be associated with a leaked activity.
The unassuming developer would pass this context object around to the threads that need it. Make some static TextViews that hold reference to the activity. You know, things that make it work? sarcasm intended, do not try those things at home
The opposite of how things will work.
A big warning sign to lookout for is the ever increasing memory usage in your app, as depicted by Androids Memory Monitor:
Android Memory Monitor running with an app that has a memory leak Android Memory Monitor after fixing the memory leak
As you can see, the first graph, the app is never able to really regain some of the memory used. It used up to about 300MB at one point before the OutOfMemoryError occurred. The second graph shows that the app is able to garbage collect, regain some memory and stays pretty consistent in its memory usage.
How do I avoid memory leaks?
- Avoid passing Context objects further that your activity or fragment
- NEVER EVER EVER make/store a Context or View in a static variable. This is the first sign of a memory leak.
- Always unregister listeners in your onPause()/ onDestroy() methods. This includes Android listeners, to things such as Location services or display manager services and your own custom listeners.
- Don’t store strong references to activities in your AsyncTasks or background threads. Your activity may get closed, but your AsyncTask will continue execution and hold onto that reference of your activity.
- Use Context-application (getApplicationContext()) instead of Context from an activity if you can.
- Try not to use non-static inner classes if you can avoid it. Storing reference to something like an Activity or View inside this can lead to memory leaks. Use WeakReference if you need to store reference to them.
How do I fix it?
Fixing memory leaks takes a bit of practise and lots of trial and error. Memory leaks can be very difficult to track down. Luckily there are a few tools that can help you identify a possible leak.
You can watch this video which shows how to obtain the HPROF file to use for analysis, or you can read the tutorial below describing how to do it.
- Open Android Studio, open the Android Monitor tab.
- Run your application, and select it from the list of available apps.
- Do some actions in your app that lead you to a similar point. For me, it was selecting a new video, and playing it about 50 times. (I wrote a test for this 🙃)
- The trick here, is to catch the app before the OutOfMemoryException.
- Click on the memory tab in Android Monitor.
- You should see a nice graph starting to draw. When you are ready, click “Initiate GC”. (The little red garbage truck)
- Click “Dump Java Heap” and wait a couple of seconds. (The icon below the truck with the green arrow). This will generate a .hprof file that you can use to analyse the memory usage.
- Unfortunately the Android Studio Hprof file viewer doesn’t have all the tooling that Eclipse Memory Analyzer has. So you will need to install MAT.
- Run the following command to convert a .hprof file from Android into one MAT will understand. (The hprof-conv tool is located in the platform tools folder of the sdk)
- Once converted, open the file in MAT. Select “Leak Suspects Report” and click finish.
Opening Eclipse Memory Analyzer – Choose Leak Suspects Report
Click on the 3 blue bars at the top “Create a histogram from an arbitrary set of objects”. You will then see a list of objects that are taking up memory.
Eclipse Memory Analyzer – Histogram
Seeing this list of objects might be a bit confusing. You can filter the objects by class name, so I suggest typing your package name in the class name filter.
Filter objects by class name in Eclipse Memory Analyzer
Now we can see that there are 9 instances of VideoDetailActivity which is obviously not right as we should only have one. To find out what is holding reference to the VideoDetailActivity , right click on the item and select “Merge Paths to Shortest GC Root”, and then click “exclude all phantom/weak/soft etc. references”. Merge Shortest Path to GC Roots – Eclipse Memory Analyzer
The thread that is holding reference will be shown. You will then be able to drill down into what exactly is holding reference to the activity.
From the below information, it is clear that there is a DisplayListener that gets registered but never unregistered. Eclipse Memory Analyzer – Memory leak Identification
So this memory leak is solved by calling unregister on the display listener that was registered.
Not all memory leaks are this easy to find, some are a lot more difficult, but hopefully this guide gets you started with trying to figure out where the problem lies and hopefully avoid potential memory leaks. There are lots of other tools that can be useful in finding memory leaks, check them out here.
How do you find and fix memory leaks? How long have you spent digging around trying to find the cause?