Memory leakage is a common but easily overlooked problem in Android development. It can cause an application to consume too many memory resources, which ultimately affects application performance and user experience. In this article, we will take a deeper look at common memory leaks in Android and provide optimization guidelines to help developers better deal with this challenge.

 What is a memory leak


Memory leakage is a phenomenon in which useless memory objects cannot be released by the system in a timely manner due to program errors or poor design during the operation of an application, resulting in wasted memory resources and degraded application performance.

 Impact of memory leaks


Memory leaks can cause applications to consume a large amount of memory resources, reducing system performance, increasing the risk of system crashes, seriously affecting user experience, and even leading to applications being forced to close by the system.

 Common Scenarios of Android Memory Leaks


  1. Lifecycle mismatch: For example, a thread holds Activity, but it is still running when the Activity is destroyed, which will result in the Activity not being recycled.

  2. Incorrect handling of static variables: If a static variable holds a reference to Activity, the reference remains after Activity is destroyed, which may result in Activity not being reclaimed.

  3. Unregistered listener: A listener is registered but not unregistered at the right time, resulting in the Activity not being recovered properly.

  4. Non-static internal class holds reference to external class: When a non-static internal class holds a reference to an external class, if the external class object is no longer in use but the internal class still holds it, the external class object cannot be garbage-collected either, resulting in a memory leak.


Several causes of memory leaks are analyzed in detail below with solutions.

 single-case leakage


The singleton pattern is characterized by ensuring that only one instance of a class exists in memory, which is usually achieved through static member variables and private constructor methods. In Android development, if a singleton object holds references to Activity or other objects with a lifecycle and doesn’t release those references at the right time, it can lead to a memory leak.

 prescription


  1. Holding Activity Objects by Weak Reference: When the singleton object holds a reference to the Activity object, you can consider using a weak reference to hold the Activity object to avoid memory leakage problems caused by strong references. This way, when the Activity object is destroyed, its weak reference will be automatically released, thus avoiding memory leaks.

  2. Releasing references that are no longer needed: The singleton object should release references to specific objects when they are no longer needed. For example, when an Activity is destroyed, the singleton object should set the reference to the Activity to null to ensure that the Activity is properly recycled.

  3. Use ApplicationContext to avoid holding Activity references: In singleton objects, try to use ApplicationContext instead of Activity references to avoid holding Activity references that can lead to memory leaks.The life cycle of ApplicationContext is longer than that of any ApplicationContext has a longer lifecycle than any Activity, so it won’t cause memory leaks.

 sample code (computing)

object MySingleton {

    private var mActivityRef: WeakReference<Activity>? = null

    fun init(activity: Activity) {
        mActivityRef = WeakReference(activity)
    }

    fun doSomething() {

        val activity = mActivityRef?.get()
        activity?.run {
            // do something
        }
    }
}

 Internal class/anonymous internal class leaks


When an inner class/anonymous inner class holds a reference to an outer class, if the outer class is a long-lived object, then even if the outer class is no longer used, the inner class cannot be reclaimed properly due to the fact that the inner class still holds a reference to the outer class, resulting in a memory leak.

 prescription


To avoid memory leakage problems caused by internal classes, the following optimizations can be taken:


  1. Use static inner classes: Declare an inner class as a static inner class so that it doesn’t hold references to the outer class, thus avoiding memory leak problems.

  2. Use weak references: When necessary, weak references can be used to hold references to external classes so that even if the external class is destroyed, it will not prevent it from being recycled.

 sample code (computing)

class MyActivity : AppCompatActivity() {
    private val mListener = MyListener(this)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        mListener.doSomething()
    }

    object MyListener(activity: MyActivity) {
        private val mActivityRef: WeakReference<MyActivity> = WeakReference(activity)
        
        fun doSomething() {
            val activity = mActivityRef.get()
            activity?.let {

                // ...
            }
        }
    }
}

 resource leakage


Resource leaks are usually caused by resources not being closed properly. For example, if resources such as files, databases, or network connections are not released in a timely manner when they are used, the resources cannot be reclaimed by the operating system, resulting in a resource leak.

 prescription


  1. Use the try-with-resources statement: For resources that need to be explicitly closed, such as file handles, database connections, etc., you can use the try-with-resources statement or Kotlin’s use function to make sure that the resource is closed correctly after use.

  2. Manually closing resources: For some resources that can’t be closed using the try-with-resources statement, such as network connections, you need to manually close the resource at the right time, usually when the resource is no longer needed or in the Activity lifecycle method.

  3. Use try-catch-finally statement: For some resources that are not amenable to a try-with-resources statement or a use function, you can use a try-catch-finally statement in a finally block to make sure that the resource is closed in any case.

 sample code (computing)


fun readFile(filePath: String): String {
    BufferedReader(FileReader(filePath)).use { reader ->
        val stringBuilder = StringBuilder()
        var line: String? = reader.readLine()
        while (line != null) {
            stringBuilder.append(line).append("\n")
            line = reader.readLine()
        }
        return stringBuilder.toString()
    }
}


fun fetchDataFromDatabase() {
    val dbHelper = DatabaseHelper(context)
    val db = dbHelper.writableDatabase
    db.query(...)

    db.close()
}


fun fetchDataFromNetwork() {
    val url = URL("https://example.com")
    var connection: HttpURLConnection? = null
    try {
        connection = url.openConnection() as HttpURLConnection

        val inputStream = connection.inputStream

    } catch (e: IOException) {
        e.printStackTrace()
    } finally {
        connection?.disconnect()
    }
}

 pooling (computing)


Collection leaks are usually caused by holding a reference to an object in a collection, but not properly removing the reference from the collection when the object is no longer needed. This often occurs in scenarios such as long-running background tasks, listeners, or caches, and can lead to memory leaks if care is not taken to release object references in collections in a timely manner.

 prescription


  1. Use weak or soft references: When you need to store long-lived objects in collections, consider using weak or soft references to hold references to the objects. This allows the object to be garbage collected even if it is no longer referenced elsewhere.

  2. Timely removal of object references: Remove references to objects from the collection when they are no longer needed to ensure that they are garbage collected. You can usually remove an object from the collection when it is no longer needed, such as in the onDestroy() method of an Activity or after a background task has finished executing.

  3. Using Android Jetpack components: Android Jetpack components provide classes for managing the lifecycle, such as ViewModel and LiveData, which help developers better manage the relationship between data and UI components and reduce the possibility of memory leaks.

 sample code (computing)

class MyActivity : AppCompatActivity() {
    private val mHashMap = WeakHashMap<String, Any>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        

        mHashMap["key"] = MyObject()
    }

    override fun onDestroy() {
        super.onDestroy()
        

        mHashMap.remove("key")
    }
}

 Context leakage


Context objects are usually associated with components such as Activity or Service and have the same life cycle. If a reference to a Context object is still held after the Activity or Service has been destroyed, it will result in the Context object not being garbage collected, which will eventually lead to a memory leak.

 prescription


  1. Use ApplicationContext: Use ApplicationContext instead of Activity or Service’s Context if you don’t need to associate it with the component’s lifecycle. applicationContext has an application-level lifecycle and doesn’t lead to memory leaks. ApplicationContext has an application-level lifecycle and does not lead to memory leaks.

  2. Avoid static variables holding Context references: Try to avoid holding Context references of Activity or Application in static variables, so as to avoid leakage caused by still holding Context references after the destruction of Activity.

  3. Use weak references: If you do need to hold a Context reference to an Activity or Application in an object, consider using weak references to hold the Context reference to ensure that it can be garbage collected when it is no longer needed.

 sample code (computing)

class MyActivity : Activity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_my)

        val context = getAppContext()

        Toast.makeText(context, "Hello, World!", Toast.LENGTH_SHORT).show()
    }
}

 Detection Tools


Of course, there are some commonly used memory leak detection tools that can help us find and solve memory leaks in time.


  1. Memory Profiler: Android Studio provides built-in tools to help monitor the memory usage of an application, including memory leaks. With Memory Profiler, you can view the application’s memory allocation, memory leaks, and analyze the causes of memory leaks to help find and fix memory leaks.

  2. LeakCanary: is an open source memory leak detection library that helps developers detect memory leaks while the application is running.LeakCanary monitors the lifecycle of Activity, Fragment, View, and other objects in the application and sends a notification when these objects are leaking, so that the developer can detect and resolve memory leaks in a timely manner. LeakCanary monitors the life cycle of

  3. MAT: MAT is a powerful Java memory analysis tool that helps developers analyze the memory usage of Java applications, including memory leaks.MAT can load the heap dump file of an Android application and provide a visual interface and rich analysis features to help developers locate and solve memory leaks.

  4. Lint tool: Lint is a static code analysis tool in Android development tools that helps developers detect potential problems in their applications, including memory leaks.Lint will statically analyze the code and issue warnings when potential memory leaks are found to help developers fix the problem in a timely manner.


Through the introduction and examples in this article, I believe you have a deeper understanding of the Android memory leak problem, and mastered some effective optimization techniques. In daily development, it is important to pay attention to memory leaks, timely detection and resolution of potential memory leaks hidden danger, in order to improve the performance and stability of the application.

By hbb

Leave a Reply

Your email address will not be published. Required fields are marked *