Showing posts with label garbage collection. Show all posts
Showing posts with label garbage collection. Show all posts

Saturday, January 16, 2016

Effective Java - Item 7 : Avoid finalizers!

Unpredictable! Dangerous! Unnecessary! The author Joshua Bloch makes sure to set the tone of the item right in the beginning by using these adjectives. To augment this view point on finalizers he says that it can be used as a rule of thumb to avoid them altogether. That is a pretty strong point of view.

Just to give you some background the finalize() method in Java is a special method of the class Object. It will be invoked by the garbage collector (GC) on an object when GC determines that there are no more references to the object i.e. just before GC reclaims the object. It sounds something similar to the destructor in C++, right? So, can it be used to cleanup any external resources like closing files? 

Wrong!

The author gives the following 3 reasons to support his point of view:

  • The promptness of the finalize methods' execution is not guaranteed.
  • There is a severe performance penalty for using finalizers.
  • Uncaught exceptions thrown by finalize method is ignored, and finalization of that object terminates.
The promptness of the finalize methods' execution is not guaranteed

The author says that there is no fixed time interval between the time an object becomes eligible for garbage collection and the time its finalizer is executed. It is dependant on the garbage collection algorithm which varies according to the JVM implementation. Further he adds that there is no guarantee that the finalize method will ever get executed at all. So now you can imagine how dangerous it is to depend on finalizers.
I was curious to find out because I have never actually made use of the finalize method in any of my code. So I wrote a small program to test it.

Here I have one class which had a FileInputStream object and a method which tries to open a file. I have overridden the finalize method where I close the file. I have added one print statement in each block (try-catch-finally-finalize) to better understand the flow of execution. I have also created another class which will create an instance of this class, invoke the openFile method and further nullify the reference to make the instance eligible for garbage collection.

 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
30
import java.io.FileInputStream;
import java.io.FileNotFoundException;


public class OpenFileTest {
 
 private FileInputStream file = null;

 public void openFile(String filename) {
  try {
   System.out.println("Inside try block");
   file = new FileInputStream(filename);
  } catch (FileNotFoundException e) {
   System.out.println("Inside catch block");
   System.err.println("Could not open file "+filename);
  } finally {
   System.out.println("Inside finally block");
  }
 }
 
 @Override
 protected void finalize() throws Throwable {
  System.out.println("Inside finalize method");
  if(file!=null){
   file.close();
   file = null;
  }
  super.finalize();
 }
}

1
2
3
4
5
6
7
8
9
public class TestFinalizeMain {

 public static void main(String[] args) {
  OpenFileTest o = new OpenFileTest();
  o.openFile("C:\\shreyas\\CusumCalc\\cusum_output.csv");
  o = null;
 }

}

Output:
Inside try block
Inside finally block

Wait, that is not what was supposed to happen. What happened to the finalize method being called by our garbage collector?
So, this is exactly what the author was talking about.

Here we can see that the finally block did get executed without any issues. So using finally block to deallocate resources is a safe bet.
There are other ways of forceful execution of the finalize method like System.gc(), but then again it just increases the odds and does not provide any guarantee.

There is a severe performance penalty for using finalizers.

The author says that the time to create and destroy an object on his machine apparently increased by 430 times when he used finalizer. Well, if you come to think of it this is actually true. The work of the garbage collector is to scan the heap often and determine which objects are no longer referenced and de-allocate the memory. But if an object uses a finalizer then the garbage collector is interrupted. And it so happens that finalizers are processed on a thread that is given a fixed, low priority. So objects that are otherwise eligible for garbage collection will be pending on finalization and use up all the available memory causing your application to slow down.

Uncaught exceptions thrown by finalize method is ignored

Now here is another good reason why you are better off without a finalizer. Any uncaught exception thrown during a finalization is ignored and it will not be propagated further and the finalization halts. Java handles uncaught exceptions by terminating the thread and usually printing the stack trace to the console. But in this case there will be no warning by means of any message being printed.

So, what is a good alternative if you want to do all your resource releasing work then? The author suggests providing an explicit termination method and requiring the clients of the class to invoke this method on each instance when it is no longer needed would be your best bet. Some good examples of such methods are the close methods on InputStream, OutputStream, and java.sql.Connection
Another option I could think of is to use the try-with-resources statement provided starting from Java 7.

Now all that said and done the question remains as what is a real good use of the finalize method? 
In some rare cases if the user forgets to call the explicit termination method of an object then a finalizer can be used as an extra level of safety to free the resource, better late than never. But the author says that it is better to use safety and precaution if you must use it and one way he suggests is to add the finalization code in a try block when you override the finalize method and invoke the super class finalizer (super.finalize()) in the finally block. This is because "finalizer chaining" is not performed automatically.

A final thought - You are better off without a finalizer!

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.