8 minutes read


If you've already started studying the garbage collection process, you must have come across the statement that an object is not deleted from memory as long as it has at least one reference. This type of reference is called a Strong Reference. However, there are types of references that are weaker and do not guarantee the life of the object. Those reference types are Soft Reference, Weak Reference, and Phantom Reference. In this topic, we will explain what they are and how they differ from ordinary strong references.

Strong Reference

As a first step, let's briefly revise the idea of a strong reference. In most cases, we are dealing with this type. As long as there is a reference to the object, it remains in memory. If there are no references, the object is available for deletion.

public class ReferenceTypesDemo {
    public static void main(String[] args) {
        Integer num = 1995;
        num = null;

        System.out.println(num); // null
    }
}

Here, we have created an object to store a number that is referenced by a single Integer variable. Once we have assigned the null value to it, nothing references the object anymore. You will no longer have access to the object and the garbage collector will delete it when it considers it unnecessary.

strong reference in diagram

This is the default reference type. If you haven't specified the reference type or haven't used a class that is implemented with a different type, the strong reference will be used by default.

Soft Reference

Objects with soft references are removed by GC only if the application is close to running out of memory and throws an OutOfMemoryError. To add a soft reference, you need to use the SoftReference class, passing the object that is intended to have the desired reference type to its constructor. It is the strongest among the non-strong references.

public class ReferenceTypesDemo {
    public static void main(String[] args) {
        Integer num = 1995;
        SoftReference<Integer> softReference = new SoftReference<>(num);
    }
}

In this example, we have created two objects, the first for an Integer, and the second for a SoftReference that reference an Integer object by a soft reference. For the first object, the term is referent and the other one is called reference object.

reference object

This technique allows you to get an Integer object even after its strong reference has been removed. Let's modify our code a bit to understand how that can happen:

public class ReferenceTypesDemo {
    public static void main(String[] args) {
        Integer num = 1995;
        SoftReference<Integer> softReference = new SoftReference<>(num);

        num = null;
        System.out.println(num); // null

        num = softReference.get();
        System.out.println(num); // 1995
    }
}

If we had only one strong reference to the Integer object, we would no longer be able to get the value of its object after assigning null to the num variable.
Now let's see how the GC deletes an object with a soft reference. Imagine your application is one step away from throwing an OutOfMemoryError. Let's analyze the in-memory sector where we have such a situation:

Stack and Heap diagram

Objects A and C will not be removed since they have strong references, whereas object B might. That is, as soon as strong references are removed from the object and only one or more soft references remain, the GC can remove the object if the application is running out of memory.

Weak Reference

A weak reference is weaker than a soft reference. While the presence of a soft reference allows the object to live until the application is almost out of memory, an object with only a weak reference can be removed during the first garbage collection after its creation. You can add this reference in the same way as you would add a soft reference and it also makes it possible to "resurrect" an object after removing its strong references.

public class ReferenceTypesDemo {
    public static void main(String[] args) {
        Integer num = 1995;
        WeakReference<Integer> weakReference = new WeakReference<>(num);

        num = null;
        num = weakReference.get();

        System.out.println(num); // 1995
    }
}

When we pass the num variable to the WeakReference constructor, the same thing happens in memory as in the case of a soft reference. We've created a reference object with a weak reference to the referent.

weak reference in heap

After the strong reference is removed from num, only the weak reference to the object remains, and it can be deleted at any moment.

Important note: if the object holds soft and weak references at the same time, it means that this object will survive garbage collections until the application runs out of memory.

Phantom Reference

Phantom is the weakest reference type. This is an advanced topic, so we will not delve into the details for now. All you need to know is that phantom reference does not set any reachability level for objects. Its get() method returns null to prevent the "resurrection" of the object since the purpose of a phantom reference is to detect when an object was removed from memory.

Conclusion

In this topic, you learned the basics about the four types of references in Java. Our goal was to provide some essential information that will help you understand the idea behind each type, but there is much more to this topic and you are welcome to explore it further. If you are interested, we recommend a read on the Reference class, a base class representing non-strong references. After that, you might want to learn about the role of a reference queue, which is represented by the ReferenceQueue class.

115 learners liked this piece of theory. 6 didn't like it. What about you?
Report a typo