Open In App

How to Solve OutOfMemoryError in Android Application?

Last Updated : 16 Sep, 2022
Improve
Improve
Like Article
Like
Save
Share
Report

OutOfMemoryError, or simply OOM, is a problem that every Android developer has encountered. If you haven’t seen an OOM in your Android application yet, you will in the near future. Memory leaks cause the OutOfMemoryError to occur in Android. So, in order to remove the OutOfMemoryError, all you need to do is take care of memory leaks! In this Geeks for Geeks article, we will solve the OutOfMemoryError in Android with the help of some practical examples. So, let’s get started with the fundamentals of Android Memory Leak.

What exactly is a Memory Leak?

When you run an application on an Android device, the Android system allocates memory to that application in order for it to function. All variable creation, function creation, activity creation, and so on take place solely in that memory. For example, if the Android System allocates 200MB to your application, your application can only use 200MB at a time. If the space allocated to the application is reduced over time, or if only a small amount of space remains, the Garbage Collector (GC) will release the memory held by variables and activities that are no longer needed. As a result, the application will gain some space once more.

GeekTip: The Garbage Collector handles the task of releasing memory automatically, so you don’t need to do anything.

However, if you write the code in such a way that it contains references to objects that are no longer required, the Garbage Collector will be unable to release the unused space and no space will be left for the application to continue working. This is known as a Memory Leak. Because of the Memory Leak phenomenon in Android, we encounter the OutOfMemoryError in Android because your code is holding the references of objects that are no longer required and the Garbage Collector is unable to perform its job and your application uses all of the space allocated to it by the Android System and is demanding more.

What are the possible causes of OutOfMemoryError?

OutOfMemoryError in Android can occur for a variety of reasons. Memory leaks that result in an OutOfMemoryError are caused by a variety of factors, including:

  1. Application of a static view/context/activity
  2. Listeners can be registered and deregistered.
  3. Inner class that isn’t static
  4. Wrong use of getContext() and getApplicationContext() Let’s go over them one by one.

1. Utilization of static views/context/activity

If you use some static views/context/activity, you will get an OutOfMemoryError (if your activities are dealing with lots of space). This is due to the fact that the view, context, or activity will be held by the application until the application is no longer alive, and thus the memory used by these will not be released by the Garbage Collector. For example, suppose you have four activities in your application: A, B, C, and D. Activity “A” is your primary activity, and you can access activities B, C, and D from it. Assume that activities B, C, and D each hold a static reference to its context, that each activity consumes 2MB, and that the total memory allocated to the application by the Android System is 4MB. As a result, when activity “B” is launched, the application will consume 2MB of memory. Now return to activity “A” and begin activity “C.” The application will now use 4MB of memory (2MB for Activity B and 2MB for Activity C). Return to activity “A” and begin activity “D.” Your app will now require 6MB (2MB for B, 2MB for C, and 2MB for D). Let’s take a look at one example of practical implementation. I’m using Bitmap in this example. My application will encounter OutOfMemoryError on my mobile device if I use more than 5 images of 1MB each. In your case, the number of images that can cause an OutOfMemoryError to occur can be greater than or less than 5. You can find the limit by adding images to the BitmapArray one at a time, and whenever you get the OOM, that is your device’s limit.

So, our application has one MainActivity, from which I can access “ActivityB” and “ActivityC.” Both of these activities, “ActivityB” and “ActivityC,” have a fixed reference to their context. As a result, Garbage Collector will not delete the space used by these activities.

Kotlin




class GfgActivityB : AppCompatActivity() {
    companion object {
        lateinit var context: Context
    }
    val gfgBitmap = ArrayList<Bitmap>()
     
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_gfgActvity2)
        context = this
        Thread(Runnable {
            try {
                // adding 3 images to gfgBitmap
                val bitmap1: Bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image1)
                val bitmap2: Bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image2)
                val bitmap3: Bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image3)
                gfgBitmap.add(bitmap1)
                gfgBitmap.add(bitmap2)
                gfgBitmap.add(bitmap3)
            } catch (e: Exception){
                Logger.getLogger(GfgActivityB::class.java.name).warning(e.toString())
            }
        }).start()
    }
}


The below code is for the Activity C

Kotlin




class GfgActivity2 : AppCompatActivity() {
    companion object {
        lateinit var gfgContext: GfgContext
    }
    val bitmapArray = ArrayList<Bitmap>()
     
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_gfgactivity2)
        gfgContext = this
        Thread(Runnable {
            try {
 
                val bitmap1: Bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image1)
                val bitmap2: Bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.image2)
                val bitmap3: Bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.gfgImage)
                bitmapArray.add(bitmap1)
                bitmapArray.add(bitmap2)
                bitmapArray.add(bitmap3)
            } catch (e: Exception){
                Logger.getLogger(GfgActivity2::class.java.name).warning(e.toString())
            }
 
        }).start()
    }
}


So, if you launch ActivityB from MainActivity, there will be no OutOfMemoryError because the limit for my mobile device is 5 images, and I am only using 3 images in ActivityB. Return to the MainActivity and launch ActivityC; you will receive an OutOfMemoryError because we are using 3 images in ActivityC, and because we are using the static context in both ActivityB and ActivityC, the reference to ActivityB is still with us, so we have 6 images total (3 from ActivityB and 3 from ActivityC), and the limit for my mobile device is 5 images. As a result, OutOfMemoryError will occur.

2. Listeners can be registered and deregistered

In Android, we use various types of listeners to detect changes such as location changes. So, remember to unregister your listener once you’ve finished using it. You then need to register the event, even if the listener is no longer needed. As a result, if the listener is not in use, it is best to unregister it. Let’s take a look at a real-world example. We will be using only a single register and a deregister to work for us, that’s it. The register() function adds an activity to an activity array, and then the unregister() function removes an activity from the activity array. The Singleton class’s code is as follows:

Kotlin




object gfgListener {
    private val someActivity = ArrayList<Activity>()
    fun someRegister(myActivity: Activity) {
        someActivity.add(activity)
    }
    fun doUnrgis(activity: Activity) {
        someActivity.remove(activity)
    }
}


If I add more than 5 images to my mobile device, the application will display an OutOfMemoryError. So, in this example, there will be three activities: MainActivity, ActivityB, and ActivityC. The activities ActivityB and ActivityC will be called from MainActivity, and in the onCreate() functions of both of these activities, we will call the Singleton class’s register() method to add the activities to the activity array. If we call ActivityB from MainActivity, the references to three images will be saved (as we are storing in the Singleton class). Because our limit is 5 images, no OutOfMemoryError will be displayed. Now, if we return to the MainActivity and call ActivityC, we will get an OutOfMemoryError because we initially have a reference to three images, and when ActivityC is launched, three more images appear, giving us a total of six images (as we are using the same Singleton class) and our limit is five. As a result, we will receive a MemoryOutOfBoundError.

GeekTip: To get rid of this error, deregister the listener when the activity is finished or no longer useful. So, when the activity is finished, add the following lines of code to unregister the listener.

Kotlin




override fun justStop() {
    AnotherListner.unregister(this)
    super.onStop()
}


3. The nested class must be static

If you have a nested class in your application, make it static because static classes do not require the outer class’s implicit reference. So, if you make the inner class non-static, it keeps the outer class alive until the application is launched. As a result, if your class consumes a large amount of memory, this can result in an OutOfMemoryError. As a result, it is preferable to make the inner class static. In Java, you must make the inner class static on your own, whereas in Kotlin, the inner class is static by default. So you don’t have to worry about static inner classes in Kotlin.

4. In third-party libraries, the functions getContext() and getApplicationContext() are used incorrectly

In our application, we use a lot of third-party libraries, and the majority of them make use of the Singleton class. So, if you’re passing some context to a third-party library that’s outside the scope of the current activity, use getApplicationContext() rather than getContext(). In general, we do the following in our application:

aThirdPartyLib.initialize(this)

In this case, initialize is a static function in that library that uses the context in the following way:

Kotlin




aThirdLib {
  object someCompanion {
    initialize(Here context) {
       this.context = context.getApplicationContext()
    }
  }
}


However, some libraries do not use the above notation. As a result, it will use the context of the current activity, and the reference to the current activity will be held until the application is alive, which may result in an OutOfMemoryError (as the initialize function is static).

GeekTip: As a result, it is preferable to use getApplicationContext() explicitly in your code rather than relying on third-party libraries to do so.

These are some of the methods for removing OutOfMemoryError from our application. It is preferable to write code that does not throw an OutOfMemoryError. Still, if your project is large and you are having difficulty locating the class responsible for OutOfMemoryError, you can use Android Studio’s memory profiler to locate the classes responsible for OutOfMemoryError. You should know how to actually utilize the laid functionalities of Android Studio Memory Profiler from here.



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads