Java WeakReference Example

0

In this article, we will see an example of WeakReference.  A weakly reachable object like unreachable object is always eligible for garbage collection. If you want to know the mechanics of reference object read here.

WeakReference

Had the reference to object B been directly accessible from the root set, object B would not have been claimable but since it is accessible through weak reference object garbage collector considers it unreachable. When the garbage collector determines that the object is weakly reachable then it clears and declares the object to be finalizable.

Weak Reference Tree

Weak Reference Tree

In the above diagram, object B is reachable only through the weak reference. It can be reached through:

  1. Weak Reference Ref(B)
  2. Through chain of strong and weak reference. A to C to D to Ref(B). Other path is C to D to Ref(B)

If the only ways to reach an object involve at least one weak reference object, the object is said to be weakly reachable.

If the garbage collector collects a weakly reachable object, all weak references to it are set to null so the object can no longer be accessed through the weak reference.

WeakReferenceExample:

package com.javarticles.references;

import java.lang.ref.WeakReference;


public class WeakReferenceExample {
    public static void main(String[] args) {
        B b = new B();
        WeakReference<B> bRef = new WeakReference<B>(b);        
        C c = new C(b);
        A a = new A(c);
        b = null;
        
        System.out.println("Run gc");
        Runtime.getRuntime().gc();
        
        System.out.println("bRef's referent:" + bRef.get());
        System.out.println("bRef's referent thru a->c->d->bRef:" + a.getC().getD().getB());
    }
 
}

A:

package com.javarticles.references;

public class A {
    private C c;
    
    public A(C c) {
        this.c = c;
    }
    
    public C getC() {
        return c;
    }
    
    @Override
    public void finalize() {
        System.out.println("A cleaned");
    }
}

B:

package com.javarticles.references;

public class B {

    @Override
    public void finalize() {
        System.out.println("B cleaned");
    }
}

C:

package com.javarticles.references;

import java.lang.ref.WeakReference;

public class C {
    private D d;

    public C(B b) {
        d = new D(new WeakReference<B>(b));    
    }

    public D getD() {
        return d;
    }
    
    @Override
    public void finalize() {
        System.out.println("C cleaned");
    }
}

D:

package com.javarticles.references;

import java.lang.ref.WeakReference;

public class D {
    private WeakReference<B> bRef;
    
    public D(WeakReference<B>bRef) {
        this.bRef = bRef;
    }
    
    public B getB() {
        return bRef.get();
    }
    
    @Override
    public void finalize() {
        System.out.println("D cleaned");
    }
}

Output:

bRef's referent:null
B cleaned
bRef's referent thru a-<c-<d-<bRef:null

Reference Queue

We have seen when an object becomes weakly reachable. If one wants to perform some action when an object is being claimed then the reference object needs to be registered against a queue. This allows finer control than the older finalization mechanism alone.

To be placed on a reference queue, a reference object must be created with a reference queue.

ReferenceQueue<Employee> queue = new ReferenceQueue<>();
new WeakReference(employees[i], queue)

When the collector determines that the referent is softly, weakly or phantom reachable, the reference object will be enqueued in the registered reference queue.
A program can then poll the reference queue to know when the referent object is reachable only through reference objects. Once found the entry in queue, the program can proceed with clean-up operations on other related objects to make them eligible for garbage collection at the same time.
Reference queue holds references to Reference subclass objects.
Reference subclass object references arrive at the end of the reference queue and exit from that queue’s front. See here for details.
As a reference is removed, the following reference becomes the head, and thus other references move forward.

Employee:

package com.javarticles.classloader;

public class Employee {
    private String name;
    
    public Employee(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
    
    public String toString() {
        return name;
    }
}

Company:

package com.javarticles.classloader;

import java.util.HashSet;
import java.util.Set;

public class Company {
    private Set<Employee> employees;
    
    public void addEmployee(Employee employee) {
        if (employees == null) {
            employees = new HashSet<>();
        }
        employees.add(employee);
    }
    
    public void removeAllEmployees() {
        employees.clear();
    }
}

When the JVM creates an object, that object stays initially reachable as long as a program maintains at least one reference to the object. Assigning null to an object reference variable reduces the object’s references by one. For example:

 ReferenceQueue<Employee> queue = new ReferenceQueue<>();
        List<WeakReference<Employee>> empList = new ArrayList<WeakReference<Employee>>();
        for (int i = 0; i < employees.length; i++) {
            empList.add(new WeakReference<Employee>(employees[i], queue));
            employees[i]= null;
        }

If the garbage collector has cleared that reference get() returns null when the weak reference clears.

Steps taken by Garbage Collector

Steps taken by Garbage Collector

In the below example, we create employees. Add employees to a company. We create a list of weak reference to each employee object. As long as the company is holding the employee objects, the garbage collector fails to reclaim. Once the employee objects are removed from the company, garbage collector manages to claim the employee objects.
Next we poll the queue to see whether now it holds the employee reference object. The reference object returns null once reclaimed.

WeakReferenceExample:

package com.javarticles.classloader;

import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public class WeakReferenceExample {
    public static void main(String[] args) {
        Employee[] employees = new Employee[]{
          new Employee("Joe"),
          new Employee("Ram"),
          new Employee("Sam"),
          new Employee("John")
        };
        Company company = new Company();
        company.addEmployee(employees[0]);
        company.addEmployee(employees[1]);
        
        ReferenceQueue<Employee> queue = new ReferenceQueue<>();
        List<WeakReference<Employee>> empList = new ArrayList<WeakReference<Employee>>();
        for (int i = 0; i < employees.length; i++) {
            empList.add(new WeakReference<Employee>(employees[i], queue));
            employees[i]= null;
        }
        
        System.out.println("Queue's polling returns null? " + (queue.poll() == null));
        
        
        reclaimEmpObjects(empList);
        
        company.removeAllEmployees();
        
        reclaimEmpObjects(empList); 
                
                
        Reference<? extends Employee> empRef = queue.poll();
        System.out.println("Poll weak emp references garbage collected");
        while (empRef != null) {        
            empRef = queue.poll();
            if (empRef != null) {
                System.out.println("WeakReference of Emp removed from queue");
            }
        }
        System.out.println("done");
    }
    
    private static void reclaimEmpObjects(List<WeakReference<Employee>> empList) {
        System.out.println("Run gc");
        System.gc();
        boolean objReclaimed = false;
        while(!objReclaimed) {
            for (int i = 0; i < empList.size(); i++) {
                if (empList.get(i).get() == null) {
                    System.out.println("employee reclaimed");                    
                    objReclaimed = true;
                }
            }
        }  
        
        Predicate<WeakReference<Employee>> notNullEmp = empRef-> empRef.get() != null;
        List<WeakReference<Employee>> newEmpList = empList.stream().filter(notNullEmp).collect(Collectors.toList());
        empList.retainAll(newEmpList);
        System.out.println("Employee un-claimed size: " + empList.size());
    }
}

Output:

Queue's polling returns null? true
Run gc
employee reclaimed
employee reclaimed
Employee un-claimed size: 2
Run gc
employee reclaimed
employee reclaimed
Employee un-claimed size: 0
Poll weak emp references garbage collected
WeakReference of Emp removed from queue
WeakReference of Emp removed from queue
WeakReference of Emp removed from queue
done

Download the source code

This was an example about Java WeakReference. You can download the source code here:

About Author

Ram’s expertise lies in test driven development and re-factoring. He is passionate about open source technologies and loves blogging on various java and open-source technologies like spring.
You can reach him at [email protected]

Comments are closed.