Is WeakReference ⛓️‍💥 really a solution to memory leaks?

it’s magic 🪄, let’s use it everywhere! or is it?

Cedric Ferry
4 min readAug 21, 2024

In the previous article we learned about memory leak, how they can occur and ways to manually free memory for the Garbage Collector.

Today, we are going to learn how to use WeakReference to avoid memory leaks.

Preventing leaks with WeakReference

A WeakReference is a reference to an object that does not prevent an object from being garbage collected. If the only references to an object are weak references, the object can be reclaimed by the garbage collector.

Strong reference

Strong reference is when an Object B reference an Object A directly. It mean Object B needs Object A to function.

class ClassA(val large: Bitmap)
class ClassStrongRef(val objA: ClassA)

val objectA = ClassA(Bitmap())
val objectB = ClassStrongRef(objectA) // objectB has a strong reference to objectA
val objectC = ClassStrongRef(objectA) // objectC has a strong reference to objectA

If an Object C is also referencing Object A then Object A can be released only if both Object B and C de-reference Object A.

Weak Reference

Weak reference is when an Object C reference an Object A using WeakReference. It means Object C use Object A but is not required / is less important.

If another Object B is also referencing Object A then Object A can be released as soon as Object B de-reference Object A. In other words, it doesn’t matter that Object C is using Object A, from the point of view of the garbage collector.

class ClassA(val large: Bitmap)
class ClassStrongRef(val objA: ClassA)
class ClassWeakReference(val weakRefObjA: WeakReference<ClassA>)

val objectA = ClassA(Bitmap())
val objectB = ClassStrongRef(objectA) // objectB has a strong reference to objectA
val objectC = ClassWeakReference(WeakReference(objectA)) // objectC has a weak reference to objectA

With WeakReference Object C, must verify that Object A is not null.

// Weak reference usage

val objectC = ClassWeakReference(WeakReference(objectA)) // objectC has a weak reference to objectA
objectC.weakRefObjA.get()?.let { objA ->
// to access the object A through WeakReference, you need to call get().
// get() can return null if the object has been garbage collected.
}

Use WeakReference when you need to hold a reference to an object but don’t want to prevent its garbage collection, for example Context object.

Notice how to access objectA, we need to call get() on weakRefObjA, this creates additional complexity. For each access to the Object, the system will first check if it is not null.

You can use WeakReference everywhere you need access to context outside an Activity, Fragment or View. Keep in mind that get() may return null, if the original object has been garbage collected. Perhaps, in this case is a good time to clean up your own object.

weakRefObj.get()?.let {
...
} ?: cleanup()

Using WeakReference may be useless if you have full control of the lifetime of an object. Below is an example where we have access to the object holding a WeakReference. In that case it is optional to use WeakReference as we can de-reference the object manually (in onDestroy).

object ResourcesUtil {
var context: WeakReference<Context>? = null

fun getString(id: Int): String {
return context?.get()?.getString(id) ?: ""
}
}

class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
ResourcesUtil.context = WeakReference(this@MainActivity)
ResourcesUtil.getString(R.string.my_string)
}
}

// when the activity is destroyed, the ResourcesUtil's context will become null
// that said, it would be better to manually clean up as below
// (and we could avoid using WeakReference altogether).

override fun onDestroy() {
ResourcesUtil.context = null // de-reference manually.
super.onDestroy()
}

WeakReference can be useful when you use Dependency Injection with context. For example with Koin with context resolution, and with Hilt @ActivityContext.

class MyClass(context: Context) // strong reference

// Koin module definition
val koinModule = module {
factory { MyClass(get()) } // <- can leak fragment
}


class MyClass(context: WeakReference<Context>) // use WeakReference

val betterKoinModule = module {
factory { (context: Context) -> MyClass(WeakReference(context)) } // <- provide WeakReference
}

Always think twice before referencing Activity, Fragment, View context, it is a frequent cause of memory leak.

I talked a lot about Context but, WeakReference can hold any type of object. WeakReference can be useful if you use objects across classes especially in a multi-thread setup.

Why not use WeakReference everywhere then?

On the surface, using WeakReferences for all object references might seem like a solution for all memory leaks. After all, if everything is weakly referenced, nothing can be held onto indefinitely, right?

Unfortunately, this approach can quickly cause problems. Objects essential for the application’s operation (like Context, Activity, View) could be garbage collected at any moment, because there is no strong reference, it means no one really needs the object.

Hence, it introduces a high degree of unpredictability into your application. Leading to unexpected crashes and unexpected behavior.

In addition, performance can suffer significantly. Constant checks for null WeakReferences (via get() function) and the overhead of the WeakReference mechanism itself can impact application responsiveness.

Finally, code becomes significantly harder to read and debug when object lifetimes are uncertain and potential null pointer exceptions lurk everywhere.

Conclusion

Today we learned how WeakReference can be useful, but it is no magic. Most of the time we can avoid using WeakReference by cautiously managing memory and explicitly setting de-referencing, in onDestroy() for example.

If you read this far, please consider clapping 👏🏼 to support the author, thank you 🙏🏼

This article is sponsored by Android Developer News, app is available on the playstore.

--

--

Cedric Ferry
Cedric Ferry

Written by Cedric Ferry

Android Developer @ TikTok, ex-Google

No responses yet