Close

Java Garbage Collection - SoftReference vs WeakReference

[Last Updated: Apr 27, 2018]

The package java.lang.ref contains classes which can wrap any arbitrary object and are used as an indication to the garbage collector that how the underlying object should be garbage collected.

For a Java program, the main advantage of using these classes is to know when an object has been garbage collected. Reference<T>is the abstract base class which has a method get(). This method starts to return null when the underlying object is garbage collected. This only happens at some point after the underlying object is not reachable by the program directly, even though Reference<T> instance is still reachable.

In this example we will analyze the difference between the two subclasses of Reference<T>: SoftReference<T> and WeakReference<T>.

WeakReference: is used to hold an object which will become eligible for the garbage collection as soon as it is not reachable by the program.

SoftReference: lives longer, it will only be garbage collected before an OutOfMemoryError is thrown.

Soft reference vs a normal reference

Let's create an Object which initializes a big array (to create a memory shortage situation). This class also overrides Object#finalize() method. finalize() method is called during a garbage collection cycle when this object has no more references in the rest of the program and is about to be garbage collected.

public class MyObject {
  private int[] ints = new int[1000];
  private final String name;

  public MyObject(String name) {
      this.name = name;
  }

  @Override
  public String toString() {
      return name;
  }

  @Override
  protected void finalize() throws Throwable {
      System.out.printf("Finalizing: %s%n", name);
  }
}

The main class:

public class SoftVsNormal {

  public static void main(String[] args) throws InterruptedException {
      List<Reference<MyObject>> references = new ArrayList<>();
      for (int i = 0; i < 10; i++) {
          //create soft reference
          MyObject myObject = new MyObject("soft " + i);
          Reference<MyObject> ref = new SoftReference<>(myObject);
          references.add(ref);
          //without wrapping in any reference
          new MyObject("normal " + i);
      }
      //let see which ones' get() will return null
      printReferences(references);
  }

  public static void printReferences(List<Reference<MyObject>> references) {
      ExecutorService ex = Executors.newSingleThreadExecutor();
      ex.execute(() -> {
          try {
              //sleep a little in case if finalizers are currently running
              Thread.sleep(1000);
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
          System.out.println("-- printing references --");
          references.stream()
                    .forEach(SoftVsNormal::printReference);
      });
      ex.shutdown();
  }

  public static void printReference(Reference<MyObject> r) {
      System.out.printf("Reference: %s [%s]%n", r.get(),
              r.getClass().getSimpleName());
  }
}

Output

Finalizing: normal 1
Finalizing: normal 0
Finalizing: normal 9
Finalizing: normal 8
Finalizing: normal 6
Finalizing: normal 5
Finalizing: normal 4
Finalizing: normal 3
Finalizing: normal 2
Finalizing: normal 7
-- printing references --
Reference: soft 0 [SoftReference]
Reference: soft 1 [SoftReference]
Reference: soft 2 [SoftReference]
Reference: soft 3 [SoftReference]
Reference: soft 4 [SoftReference]
Reference: soft 5 [SoftReference]
Reference: soft 6 [SoftReference]
Reference: soft 7 [SoftReference]
Reference: soft 8 [SoftReference]
Reference: soft 9 [SoftReference]

The above output shows that normal objects are garbage collected but all soft references are still reachable till the end.

we ran our main class will limited runtime memory by setting JVM option:
-Xmx1m -Xms1m.

In all examples in this tutorials, we will use the same memory settings. The output are taken from Intellij IDE.

Note that above output is different on different runs. If you are not going to have similar outcome in your machine or you see OutOfMemoryError error, you will need to tweak -Xmx and -Xms options.

A quick description what we did above:

The main class creates multiple SofReferences in a loop and let go their underlying references unreachable when the current loop iteration ends.

We are also creating the same object types without wrapping them in the SoftReferences (we are naming these object as 'normal' in the example), this is to compare 'the two', in terms of garbage collection when the program is low in memory.

We are keeping the SoftReferences in a collection so that we can afterward check how many of them will return null on get() call. Keeping an instance of Reference in a collection (or whatever means) does not create a strong reference of the underlying object. If we kept the 'normal' objects straight in a collection, then for sure we would be creating strong references. In all examples, we will purposely be letting MyObject references to go out of reach to see how WeakReference/SoftReference work.

Weak reference vs a normal reference

Let's repeat the same but this time with WeakReferences:

public class WeakVsNormal {

  public static void main(String[] args) {
      List<Reference<MyObject>> references = new ArrayList<>();
      for (int i = 0; i < 10; i++) {
          MyObject myObject = new MyObject("weak " + i);
          Reference<MyObject> ref = new WeakReference(myObject);
          references.add(ref);
          new MyObject("normal " + i);
      }
      SoftVsNormal.printReferences(references);
  }
}

Output

Finalizing: normal 9
Finalizing: weak 9
Finalizing: weak 2
Finalizing: normal 1
Finalizing: weak 1
Finalizing: normal 0
Finalizing: normal 6
Finalizing: weak 6
Finalizing: normal 5
Finalizing: weak 5
Finalizing: normal 4
Finalizing: normal 3
Finalizing: normal 2
Finalizing: normal 8
Finalizing: weak 8
Finalizing: normal 7
Finalizing: weak 7
-- printing references --
Reference: weak 0 [WeakReference]
Reference: null [WeakReference]
Reference: null [WeakReference]
Reference: weak 3 [WeakReference]
Reference: weak 4 [WeakReference]
Reference: null [WeakReference]
Reference: null [WeakReference]
Reference: null [WeakReference]
Reference: null [WeakReference]
Reference: null [WeakReference]

This time most of the WeakReferences are garbage collected as soon as they become unreachable, just like normal objects.

Soft vs Weak references

public class SoftVsWeak {

  public static void main(String[] args) {
      List<Reference<MyObject>> references = new ArrayList<>();
      for (int i = 0; i < 10; i++) {
          Reference<MyObject> ref = new WeakReference<>(
                  new MyObject("weak " + i));
          references.add(ref);
          ref = new SoftReference<>(
                  new MyObject("soft " + i));
          references.add(ref);
      }
      SoftVsNormal.printReferences(references);
  }
}

Output

Finalizing: weak 2
Finalizing: weak 1
Finalizing: weak 6
Finalizing: weak 5
Finalizing: weak 4
Finalizing: weak 3
Finalizing: weak 7
-- printing references --
Reference: weak 0 [WeakReference]
Reference: soft 0 [SoftReference]
Reference: null [WeakReference]
Reference: soft 1 [SoftReference]
Reference: null [WeakReference]
Reference: soft 2 [SoftReference]
Reference: null [WeakReference]
Reference: soft 3 [SoftReference]
Reference: null [WeakReference]
Reference: soft 4 [SoftReference]
Reference: null [WeakReference]
Reference: soft 5 [SoftReference]
Reference: null [WeakReference]
Reference: soft 6 [SoftReference]
Reference: null [WeakReference]
Reference: soft 7 [SoftReference]
Reference: weak 8 [WeakReference]
Reference: soft 8 [SoftReference]
Reference: weak 9 [WeakReference]
Reference: soft 9 [SoftReference]

Some Weak references were garbage collected but soft references were not garbed collected till the end of the execution.

Soft vs Weak vs Normal references

public class SoftVsWeakVsNormal {

  public static void main(String[] args) {
      List<Reference<MyObject>> references = new ArrayList<>();

      for (int i = 0; i < 10; i++) {
          //weak
          Reference<MyObject> ref = new WeakReference<>(
                  new MyObject("weak " + i));
          references.add(ref);
          //soft
          ref = new SoftReference<>(
                  new MyObject("soft " + i));
          references.add(ref);
          //normal
          new MyObject("normal " + i);
      }
      SoftVsNormal.printReferences(references);
  }
}

Output

Finalizing: weak 4
Finalizing: normal 2
Finalizing: weak 2
Finalizing: normal 1
Finalizing: normal 0
Finalizing: weak 0
Finalizing: normal 9
Finalizing: weak 9
Finalizing: weak 6
Finalizing: normal 5
Finalizing: weak 5
Finalizing: normal 4
Finalizing: normal 3
Finalizing: normal 8
Finalizing: weak 8
Finalizing: normal 7
Finalizing: normal 6
-- printing references --
Reference: null [WeakReference]
Reference: soft 0 [SoftReference]
Reference: weak 1 [WeakReference]
Reference: soft 1 [SoftReference]
Reference: null [WeakReference]
Reference: soft 2 [SoftReference]
Reference: weak 3 [WeakReference]
Reference: soft 3 [SoftReference]
Reference: null [WeakReference]
Reference: soft 4 [SoftReference]
Reference: null [WeakReference]
Reference: soft 5 [SoftReference]
Reference: null [WeakReference]
Reference: soft 6 [SoftReference]
Reference: weak 7 [WeakReference]
Reference: soft 7 [SoftReference]
Reference: null [WeakReference]
Reference: soft 8 [SoftReference]
Reference: null [WeakReference]
Reference: soft 9 [SoftReference]

Weak references and normal objects are equally likely to be garbage collected but soft references live longer than them.

Soft reference only

In all above examples, we never saw a SoftReference was ever garbage collected. That's because GC will keep them as long as possible. GC will only start removing them when program has no more memory available and OutOfMemoryError situation is reached. At that point, GC will remove as many of them to avoid OutOfMemoryError.

Let's increase the number of iteration of our loop this time.

public class SoftOnly {

  public static void main(String[] args) throws InterruptedException {
      List<Reference<MyObject>> references = new ArrayList<>();
      for (int i = 0; i < 100; i++) {
          MyObject myObject = new MyObject("soft " + i);
          Reference<MyObject> ref = new SoftReference<>(myObject);
          references.add(ref);
          //allocate a little slowly
          Thread.sleep(10);
      }
      SoftVsNormal.printReferences(references);
  }
}

Output

Finalizing: soft 25
Finalizing: soft 7
Finalizing: soft 6
Finalizing: soft 5
Finalizing: soft 4
Finalizing: soft 3
Finalizing: soft 2
Finalizing: soft 1
Finalizing: soft 0
Finalizing: soft 16
Finalizing: soft 15
Finalizing: soft 14
Finalizing: soft 13
Finalizing: soft 12
Finalizing: soft 11
Finalizing: soft 10
Finalizing: soft 9
Finalizing: soft 8
Finalizing: soft 24
Finalizing: soft 23
Finalizing: soft 22
Finalizing: soft 21
Finalizing: soft 20
Finalizing: soft 19
Finalizing: soft 18
Finalizing: soft 17
Finalizing: soft 50
Finalizing: soft 49
Finalizing: soft 48
Finalizing: soft 47
Finalizing: soft 46
Finalizing: soft 45
Finalizing: soft 44
Finalizing: soft 43
Finalizing: soft 42
Finalizing: soft 34
Finalizing: soft 33
Finalizing: soft 32
Finalizing: soft 31
Finalizing: soft 30
Finalizing: soft 29
Finalizing: soft 28
Finalizing: soft 27
Finalizing: soft 26
Finalizing: soft 41
Finalizing: soft 40
Finalizing: soft 39
Finalizing: soft 38
Finalizing: soft 37
Finalizing: soft 36
Finalizing: soft 35
-- printing references --
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: null [SoftReference]
Reference: soft 51 [SoftReference]
Reference: soft 52 [SoftReference]
Reference: soft 53 [SoftReference]
Reference: soft 54 [SoftReference]
Reference: soft 55 [SoftReference]
Reference: soft 56 [SoftReference]
Reference: soft 57 [SoftReference]
Reference: soft 58 [SoftReference]
Reference: soft 59 [SoftReference]
Reference: soft 60 [SoftReference]
Reference: soft 61 [SoftReference]
Reference: soft 62 [SoftReference]
Reference: soft 63 [SoftReference]
Reference: soft 64 [SoftReference]
Reference: soft 65 [SoftReference]
Reference: soft 66 [SoftReference]
Reference: soft 67 [SoftReference]
Reference: soft 68 [SoftReference]
Reference: soft 69 [SoftReference]
Reference: soft 70 [SoftReference]
Reference: soft 71 [SoftReference]
Reference: soft 72 [SoftReference]
Reference: soft 73 [SoftReference]
Reference: soft 74 [SoftReference]
Reference: soft 75 [SoftReference]
Reference: soft 76 [SoftReference]
Reference: soft 77 [SoftReference]
Reference: soft 78 [SoftReference]
Reference: soft 79 [SoftReference]
Reference: soft 80 [SoftReference]
Reference: soft 81 [SoftReference]
Reference: soft 82 [SoftReference]
Reference: soft 83 [SoftReference]
Reference: soft 84 [SoftReference]
Reference: soft 85 [SoftReference]
Reference: soft 86 [SoftReference]
Reference: soft 87 [SoftReference]
Reference: soft 88 [SoftReference]
Reference: soft 89 [SoftReference]
Reference: soft 90 [SoftReference]
Reference: soft 91 [SoftReference]
Reference: soft 92 [SoftReference]
Reference: soft 93 [SoftReference]
Reference: soft 94 [SoftReference]
Reference: soft 95 [SoftReference]
Reference: soft 96 [SoftReference]
Reference: soft 97 [SoftReference]
Reference: soft 98 [SoftReference]
Reference: soft 99 [SoftReference]

This time some of the SoftReferences were garbage collected.

Conclusion

SoftReference: The underlying object can survive garbage collection's several cycles as long as the JVM is able to recover enough memory without throwing OutOfMemoryError. This makes them good candidate for keeping a memory-sensitive cache.

WeakReference: The underlying object can be garbage collected just like a normal object. It can be useful for situation when an object is used multiple times repeatedly during an event, but then, will likely to be used after a long time.

In both above cases, the program can recreate the underlying object whenever get() method returns null. This feature makes them being useful when comparing to normal object references.

Example Project

Dependencies and Technologies Used:

  • JDK 1.8
  • Maven 3.3.9

Soft Vs Weak Example Select All Download
  • soft-vs-weak-ref-examples
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • SoftVsWeak.java

    See Also