I’ve heard about weak references for the first time when I was reading more in LeakCanary library’s documentation to see how it works. But I didn’t quite get it when I googled it; Also, all of the examples I read weren’t practical which made it harder to grasp the concept.
There are 4 types of references in Java: Strong, Soft, Weak, and Phatom.
We will focus on Strong and Weak references in this article.
Strong Reference
It is the reference that gets created by default whenever we create any object. This reference will prevent the garbage collector (GC) from destroying this object.
So for example, when we create an instance of a class called Dummy
; Like in the following image:
A new object of type Dummy
gets created in memory and myObject
has a strong reference to it.
When we try to print myObject
before garbage collector runs; It will print the address of the object in memory.
But, what happens when garbage collector runs? (We simulate this by forcing GC to run by writing System.gc()
)
The object will stay in the memory and nothing will happen as this is what we expected (Strong references prevents GC from destroying the referenced object).
Weak Reference
Here is its definition from Wikipedia:
A weak reference is a reference that does not protect the referenced object from collection by a garbage collector, unlike a strong reference.
Which means that when we create a weak reference to an object, and garbage collector runs, it will clear the object from memory right away (If there are no other strong references to this object).
Lets take this example and then I will explain when we may need such thing.
In the previous image, we created an object of type Dummy
and myObject
has a weak reference to it.
So before garbage collector runs, There is now an object of type Dummy
in memory which is referenced by myObject
using a weak reference.
We will now print that Dummy
object to make sure that it exists in memory; using .get()
method on myObject
variable.
Note: The
get()
method returns the referent that theWeakReference
refers to or returns null if the referenced object is cleared.
So the memory address of the Dummy
object we created is printed in the console which confirms that the object is in memory now.
But, what happens if we run the garbage collector now?
Here, you can see that null
is printed and the Dummy
instance is removed from memory; which makes sense as the description of the weak references says that if there is a weak reference to an object and garbage collector runs, it will remove this object from memory.
Example using both Strong & Weak references
For this example, we have a variable myObject
which holds a strong reference to an Dummy
instance.
So lets see what happens before and after garbage collector runs.
Here, The Dummy()
object in memory doesn't get destroyed after GC runs as there is a strong reference (myObject
) that references it.
And for the weak reference, it doesn't get cleared by GC as the object (Dummy()
) has another strong reference (myObject
) that is attached to it.
Which is the real benefit of weak references, they only keep a reference to an object as long as it exists in memory and gets cleared when all the strong references related to this object gets cleared.
Real World Example in Android
We all know that it is a bad idea to keep a reference to a context in singletons. If you try to do so, Android Studio will give you a warning stating that:
Do not place Android context classes in static fields (static reference to MySingleton which has field context pointing to Context); this is a memory leak
And this can cause memory leaks.
So, how to solve this?
Using weak references!
Here is how to use it:
Now, the warning disappears as it doesn't cause a leak, because when the MainActivity
instance gets destroyed (onDestroy
gets called), this weak reference to its context will get destroyed too; so no memory leaks occur.
Conclusion
WeakReference is a very important concept to have in your toolbox when developing Android apps, libraries, or any Java/Kotlin projects; However, you have to make sure you use it correctly in order to avoid making any unexpected behavior in your code.