Skip to content

Instantly share code, notes, and snippets.

@Mercandj
Last active April 5, 2022 15:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Mercandj/7d0346b0ebf2a42251a98a88abfbbf9a to your computer and use it in GitHub Desktop.
Save Mercandj/7d0346b0ebf2a42251a98a88abfbbf9a to your computer and use it in GitHub Desktop.

Android: Memory leak

Java and the JVM may leak objects.

Leaking an object on Java (or Kotlin) is done when a reference direct or indirect (via transitivity) is kept so that the Garbage Collector cannot release the memory.

What Meaning in this snippet
live Means, cannot be garbage collected
died Means, is garbage collected
can be kill Means, can be garbage collected. Indeed the garbage is free to collect or not
ø Means does not have "string' reference on it ("strong" to oppose to weak)

I. Kotlin JVM Playground

class A
var a: A? = A()
// Here, "a" live
class A
var a: A? = A()
a = null
// Here, "a" can be killed
class A {
   var b: B? = null
}
class B
var b = B()
var a: A? = A()
a!!.b = b
a = null
// Here, "a" can be killed, "b" lives
class A {
   val b = B()
   
   init {
      b.setListener { 
      }
   }
}

class B {
    var listener: Listener? = null
    interface Listener { fun onChanged() }
}

var a: A? = A()
a = null
// Here, "a" can be killed

Playground conclusion:

What Leak status
A -> B -> C Until A lives, B and C live
A ø B -> C Until A lives, B and C may die
A -> B <-> C Until A lives, B and C live
A ø B <-> C Until A lives, B and C may die

II. Kotlin Android Playground

II.a. Activity VS View

Activity.onCreate() {
    textView.setOnClickListener {
        // ...
    }
}
Activity.onDestroy() {
    // Nothing here
}

Activity will not leak and can be garbage collected. Why?

  • Because the onClickListener instantiated via the syntax setOnClickListener { } is a field of the textView.
  • So, in term of reference links, we have Application ø Activity -> TextView -> OnClickListener -> Activity
    • Why Application ø Activity?
      • Because the application does not keep strong reference on Activity otherwise it will leak. The system (and the application) communicate with the activity without the reference system but with intents.
    • Why Activity -> TextView?
      • Because the textView has been "bind" so the activity has a reference to the textView
    • Why TextView -> OnClickListener?
      • Because textView.setOnClickListener { is the same as textView.setOnClickListener(object: View.OnClickListener {.
      • Because the textView is keeping this listener argument as a field (indirect direct field)
    • Why OnClickListener -> Activity?
      • Because when you do that, inside the listener, you have "2 this", the listener and the activity
textView.setOnClickListener(object : View.OnClickListener {
    override fun onClick(v: View) {
        this // The onClickListener
        this@Activity // The activity. Indeed, the anonymous instance of onClickListener is like an inner class
    }
})

That means the Activity can be killed without leaking.

And hopefully, I personnaly never see any android dev calling textView.setOnClickListener(null) on the onDestroy to avoid leak.

Same logic for the addTextChangedListener and every listener inside views, adapter or anything else instiated and host on activity.

II.b. Activity VS "Application" manager

Activity.onCreate() {
    val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
    connectivityManager.addDefaultNetworkActiveListener {
        // ...
    }
}

Here the activity will leak. Why? Application ø Activity <-> ConnectivityManager <- Application

Indeed, the system service are manager that will survive the Activity lifecycle.

Another example of leak to be sure we agree on it:

Activity.onCreate() {
    textView.setOnClickListener(onClickListener)
}

Activity.companion object {
   val onClickListener = View.OnClickListener { /* ... */ }
}

but with a onDestroy() { textView.setOnClickListener(null) }, no more activity leak.

To conclude

Fix a memory leak is not only remove reference via nullification, it's also understanding lifecycle and reference graph. Advice: test and monitor.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment