Example of java cache and types of reference to object in Java

Let's try to write a simple implementation of the memory cache.

To make it simple, we will try to use the standard HashMap, and our cash will save images.

Our code:

private Map cache = new HashMap();

/**
 * This code is used to load the images used by our class.
 * If an image has already been accessed it may be cached, 
 * so look in the cache first. If it's not in the cache, 
 * load it from the disk.
 */
public ImageIcon getImage(String imageURL)
{
  Object cachedItem = cache.get(imageURL);
  if (cachedItem == null)
  {
    // the item wasn't found in the cache, so we'll have to
    // get it manually
    final ImageIcon imageIcon;
    try
    {
      imageIcon = new ImageIcon(imageURL);
      cache.put(imageName, imageIcon);
      return imageIcon;
    }
    catch (Exception e)
    {
      throw new RuntimeException("Cannot find image. It must be on the classpath.",e);
    }
  }
  return (ImageIcon)cachedItem;
}

You may have noticed that our cache is never cleared, which may lead to classic memory leak: objects never get dereferenced, so the garbage collector can never reclaim them ..

How can we solve the problem?

1. Add deregistration method. And we will hope, that somebody will call to it, removing old pictures from the collection.
2. Use WeakHashMap instead of HashMap.

 
private Map cache = new WeakHashMap();
 

With this one change, we have now enabled our cache to automatically release objects to the garbage collector once those objects are no longer referenced anywhere else in the application.

For understanding, how it works, let’s analyze a java package (java.lang.ref ), which allowed us to solve our problem so easily.

 

java.lang.ref

This package contains classes giving reference to objects, that don’t exist in memory also guarantee close cooperation with garbage collector.

Before this package appeared, it was possible to create only strong references to objects, which were accessible, until garbage collector doesn’t clear memory allocated to the object.

Object obj = new Object();

 

I would like to remind, that garbage collector algorithm changes all the time. Therefore, we don’t have any guaranties, that the object will be cleaned immediately after deleting.

The process of cleaning can be started after number of running of garbage collector. And the process may never start, suppose that the program finishes before the garbage collector starts cleaning memory from the objects that are not used.

java.lang.ref provides three classes, that allow to implement close cooperation with the garbage collector, without interfering with it: SoftReferenceWeakReference and PhantomReference.

 

Basic terms:

Strongly reachable – it is possible to get an access to this object only via strong reference.

Softly reachable  - it is impossible to get an access to the object via strong reference, it can be accessible only via SoftReference.

 

Weakly reachable  - cannot be accessible via strong and weakly reference. To this object will access only via WeakReference.

 

Phantomly reachable  - object, therefore cannot be reachable via SoftReference and WeakReference, because its usage ended. It is reachable only via PhantomReference reference.

 

Clear – setting of object’s fields to null and determination of the object as finalizable.

Class SoftReference

Soft reference objects are cleared at the discretion of the garbage collector in response to memory demand. Soft references are most often used to implement memory-sensitive caches.

SoftReference<Rectangle> rect = new SoftReference<Rectangle>(new Rectangle());

 

Class WeakReference

Weak references usable to objects, that have long lasting life, or can be recreated very easily. The difference is that object with weak reference can be cleared in any running of garbage collector, that means memory demand is not a necessary condition. Weak references are most often used to implement canonicalizing mappings.

WeakReference<Rectangle> rect = new WeakReference<Rectangle>(new Rectangle());

 

Class PhantomReference

Phantom references are most often used for scheduling pre-mortem cleanup actions in a more flexible way than is possible with the Java finalization mechanism. Or in case that it is necessary to save trace of an object in the memory after cleaning by garbage collector. Phantom references must be used with ReferenceQueue class. After an object is cleared from memory, it is enqueued to ReferenceQueue

PhantomReference<Rectangle> rect =  new PhantomReference<Rectangle>(new Rectangle(), queue);

Queues are also used with soft and weak references. Their main function is collecting references to objects in ReferenceQueue after the object is cleaned.

That mechanism allows to remove empty references.

 

Main deference of phantom from others types of references is impossibility of object undeleting. Because phantom reference is enqueued only after object cleaning. In other cases, reference is enqueued until finalize() is called, therefore it is possible undelete the object, but reference to it must be deleted anyway.

Illustration of reference work in the memory

MyObject obj = new MyObject();                  //1
ReferenceQueue rq = new ReferenceQueue();       //2
WeakReference wr = new WeakReference(obj, rq);  //3

 

Let’s try to receive the reference back

wr.get();   //return strong reference to MyObject
rq.poll();  //return null

 

It is important to understand, that obj is a strong reference, therefore, it must be destroyed to receive desired effect.

obj = null;
System.gc();

Let’s try to receive the reference back, now

wr.get();   //return null
rq.poll();  //return reference to object WeakReference

 

 

 

 

Example of program:



 

import java.lang.ref.WeakReference;
 
public class ReferenceTest {
	public static void main(String[] args) throws InterruptedException {
 
		Student s1 = new Student(1);
		System.out.println(s1);
		WeakReference<Student> ws = new WeakReference<Student>(s1);
		System.out.println(ws.get());
		s1 = null;
		System.gc();
		Thread.sleep(1000);
 
		System.out.println(ws.get());
	}
}
 
class Student {
	public Student(int id) {
		this.id = id;
	}
 
	int id;
 
	public String toString() {
 
		return "[id=" + id + "]";
 
	}
}

Examples of using

WeakReference

We want to save additional information about of object, for example, its serial number.

 

The standard solution will be using of HashMaps, where key is our object, and value is additional field, that we want to save.

serialNumberMap.put(widget, widgetSerialNumber); 

But then, the reference to object will always exist, and garbage collector will never get access to it.

Therefore, other solution is required. One of posible solutions can be WeakHashMap,as in our first example

 

PhantomReference

Allows us to determine the exact moment of removing an object from memory. It can be needed in work with very big images, when it is required to clean completely image from memory, before another image starts loading, to prevent of OutOfMemoryError.

 

 

Using of different types of Java references, allows to reduce probability of memory leaks, as well as use memory more effectively. To implement the cache class WeakHashMap can be used.

 

 

 

 

 

 

References: http://download.oracle.com/javase/1.4.2/docs/api/java/lang/ref/Reference.html http://www.devdaily.com/blog/post/java/how-use-java-weakhashmap-class-example http://www.javaportal.ru/java/articles/referenceclasses.html (rus)

Developer