Tuesday, October 27, 2015

Effective Java - Item 6 : Eliminate obsolete object references

Before we jump into the item let us look at a very important feature offered by Java, garbage collection. 
Basically garbage collection in Java does the job of free() in C and delete() in C++. But unlike C and C++, garbage collection is done automatically in Java. The garbage collector will look for objects which are no longer being used by the program and gets rid of them thus freeing the memory so that it can be allocated to new objects.

So what exactly is an obsolete reference?
It is simply a reference which will never be dereferenced again i.e. it will be kept around in the memory though it will never be used, preventing the object it refers to from being eligible for garbage collection. And if an unused object is not garbage collected it causes a memory leak.

So how do we avoid them?
The author gives a very good example of a simple stack implementation to explain where obsolete references can be created and how to avoid creating them.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Stack {
 private Object[] elements;
 private int size = 0;
 private static final int DEFAULT_INITIAL_CAPACITY = 16;
 
 public Stack() {
  elements = new Object[DEFAULT_INITIAL_CAPACITY];
 }
 
 public void push(Object e) {
  ensureCapacity();
  elements[size++] = e;
 }
 
 public Object pop() {
  if (size == 0)
   throw new EmptyStackException();
  return elements[--size];
 }
 
 /**
 * Ensure space for at least one more element, roughly
 * doubling the capacity each time the array needs to grow.
 */
 private void ensureCapacity() {
  if (elements.length == size)
  elements = Arrays.copyOf(elements, 2 * size + 1);
 }
}
The above code works perfectly well, but there is a memory leak present. Whenever we want to push an element to the stack we create and pass an object reference to the push() function, but when we pop an element we are just returning the topmost object in the elements array and reducing the size of the stack. The object reference will still be present in the array but will never be used again since it is popped out of the stack thus creating an obsolete reference.

In order to fix this problem the author suggests using the basic method to dereference an object i.e. by nulling the reference. So the modified pop function should look like this
1
2
3
4
5
6
7
public Object pop() {
 if (size == 0)
  throw new EmptyStackException();
 Object result = elements[--size];
 elements[size] = null; // Eliminate obsolete reference
 return result;
}

Another scenario where memory leaks can happen is when we use caches. A cache is an area of local memory that holds a copy of frequently accessed data that is otherwise expensive to get or compute, e.g.: A result of a query to a database or a disk file.
Here is a good resource I found on how to create a simple in-memory cache in Java.
The author tells that it is common for programmers to put object references into a cache and forget about it and leave it there long after it becomes irrelevant. So as a solution he suggests using WeakHashMap to implement a cache. It is an implementation of Map with keys which are weak references i.e. a key/value mapping is removed when the key is no longer referenced.
Another solution the author suggests is to clear the cache periodically by a background thread or as a side effect of adding new entries to the cache.

Now let us look at a third scenario where memory leaks can happen because of obsolete references. Say that I have an object A, which calls object B to perform some database update. Once B's function completes it calls a callback function in A. So now B remains in memory until A completes, creating a memory leak. So as a solution we can explicitly deregister for the callback and remove the reference of B in the class A. Something like this :
1
2
b.removeListener(this);
b = null;
This will ensure that the JVM will be informed that no references exists to class B and garbage collector will remove it from the memory.

The author concludes by telling that memory leaks are not very easily noticed as they do not show up as obvious failures so it is always good to know where they occur commonly and avoid them in the first place.

No comments:

Post a Comment