The Java Explorer

Tips and insights on Java

  • Subscribe

  • If you find this blog useful, please enter your email address to subscribe and receive notifications of new posts by email.

    Join 37 other followers

Posts Tagged ‘constructors’

Exceptional exceptions

Posted by Eyal Schneider on December 27, 2009

From time to time, I find myself wondering what happens when an exception is thrown from some exotic place in the code. It is not always trivial how these exceptions are handled, and what are the consequences. In this post I summarize some of these cases.

Exception in static initialization code

Static initialization occurs when a class is loaded by a class loader. Its code can either come in the form of a static block (i.e. static{ ..}), or as a call to  a static method for initializing a static data member. In both cases, checked exceptions are not allowed by the compiler. When an unchecked exception occurs, on the other hand, it is wrapped by the ExceptionInInitializerError, which is then thrown in the context of the thread that triggered the class loading. The following code snippet produces this scenario:

 
class Useless {
    static {
        if (true) // Bypasses the compiler check for obvious exceptions in initializers
            throw new RuntimeException();
    }
}  

public class TestUselessClass {
    public static void main(String[] args) {
        try {
            new Useless(); // Triggers class loading
        } catch (ExceptionInInitializerError e) {
            e.printStackTrace();
        }
        new Useless();
    }
} 

 

Since class loading is lazy, the class gets loaded and initialized only when we try to create an instance from it. The class loading fails because initialization can’t complete, and then the ExceptionInInitializerError is thrown. In line 16 we try to use the class anyway. The class loading will not be attempted again, and this time we get a NoClassDefFoundError. This error is more severe than ClassNotFoundException, and it indicates in this case that we are trying to use a class that is in an erroneous state due to a failed initialization.              

It is important to note that in some cases the exception is not wrapped by ExceptionInInitializerError. If in line 5 we chose to throw an Error instead of a RuntimeException, then the JVM would have thrown the original exception without wrapping it.              

Summing up, we should be careful with complex static initializers, and be aware that an exception during initialization makes the class (and possibly the whole application) useless. Unless we want to report a fatal situation, we should not throw a runtime exception from a static initializer.

Exceptions in constructors

 

Exceptions in constructors are not rare at all. A checked exception can be used to indicate a “legitimate” problem when trying to create an instance, while an unchecked exception typically indicates a bug either in the client code or in the constructor itself. In both cases, the object is actually allocated in the heap space, but a reference to it is not returned. The object remains in a partially initialized state, until it gets garbage collected.  We learn from these facts that saving a reference to the object from the constructor itself (by using “this” reference) is a risky thing, since we may give access to an object in an invalid state.              

Another interesting thing to note about exceptions in constructors is related to reflection. When we need to invoke the empty constructor using a class object clzz, we sometimes use the method clzz.newInstance(). Reading its documentation reveals something unusual: any exceptions thrown by the constructor are propagated without a change. In other words, the newInstance method may throw checked exceptions that it does not even declare (!). We can exploit this flaw in order to bypass the compiler’s exception checking: 

 
public class UnsafeThrower {
    private static Exception toThrow; 

    private UnsafeThrower() throws Exception{
        throw toThrow;
    } 

    public static synchronized void throwExc(Exception e){
        toThrow = e;
        try {
            UnsafeThrower.class.newInstance();
        } catch (InstantiationException e1) {
            throw new IllegalArgumentException("Can't deal with InstantiationException");
        } catch (IllegalAccessException e1) {
            throw new IllegalArgumentException("Can't deal with IllegalAccessException");
        } finally{
            toThrow = null;
        }
    }
} 

 

Since newInstance declares two checked exceptions (InstantiationException and IllegalAccessException), the trick can not work with these two exceptions, and we get an IllegalArgumentException instead.           

To summarize this, we should avoid using Class.newInstance, since it does not handle exceptions correctly. Instead, we should use Constructor.newInstance, which generates an InvocationTargetException wrapper for any constructor exception.

Exceptions in a finally block 

 

A finally block is executed immediately after the try block finishes executing, either gracefully or with an exception. Any returned value or exception produced by the finally block overrides any returned value or exception ending the try block previously.    

The following flawed method implementation illustrates such a scenario:  

 
public static int countLines(String filePath) throws IOException{
    int count = 0;
    BufferedReader br = null;
    try{
        br = new BufferedReader(new FileReader(filePath));   
        while (br.readLine()!=null)
            count++;
    }finally{
     br.close(); //May cause a NullPointerException!
    }
    return count;
}

 

The method countLines in supposed to throw an IOException for all cases where an IO related problem is encountered. However, when the file is not found we get a NullPointerException instead, since it hides the original IOException being thrown. Obviously, The solution is to add a null verification on br in line 10.  

Exceptions in finalizers

Finalizers are intended for disposing of system resources or doing other kind of cleanup. We implement a finalizer by overriding Object.finalize().
What happens when we throw an exception from within an implementation of the finalize() method?  

The documentation of Object.finalize() states that:  

“Any exception thrown by the finalize method causes the finalization of this object to be halted, but is otherwise ignored.”   

 In other words, the thrown exception is ignored, and the object will eventually be garbage collected (assuming that no finalizer made this object reachable again).  

Let’s put this statement to the test:  

   
public class FinalizerTester {
    public void finalize() {
        throw new RuntimeException("Halting the finalizer...");
    }  

    public static void main(String[] args) {
        while (true)
            new FinalizerTester();
    }
}  

 

We would expect this code to run indefinitely, performing GC continuously. However, running this code on my machine ends with a OutOfMemoryError. Does this prove that the documentation is wrong, and an exception in the finalizer does prevent the object from being garbage collected after all?  

Inspecting the error’s stack trace supplies a clue of what really happens:  

Exception in thread “main” java.lang.OutOfMemoryError: Java heap space
    at java.lang.ref.Finalizer.register(Finalizer.java:72)
    at java.lang.Object.<init>(Object.java:20)
    at exceptions.FinalizerTester.<init>(FinalizerTester.java:3)
    at exceptions.FinalizerTester.main(FinalizerTester.java:10)
  

Whenever an Object with a non-empty finalizer is created, the JVM registers the object in the finalizer queue. The registration does not actually add the object to the queue; it only makes sure that the object enters the queue later, if and when it is no longer reachable . A dedicated thread (called “Finalizer” in Sun’s JVM) works in the background , removes objects from the queue, and executes their finalizers. Sometimes the rate at which objects get ready for finalization is greater than the rate at which the finalizer thread empties the queue.  As a consequence, the queue may explode and cause a OutOfMemoryError. In our case, this is exactly what happens: we slowed down the dequeue rate by throwing an exception, which is a relatively expensive operation. In fact, any other time-consuming task inside finalize() can have the same effect. 

We can conclude that:

1) An exception in an object’s finalizer may cause the cleanup to be incomplete, but it does not make the object non-collectable by the GC.
2) There are many good reasons for avoiding the usage of finalizers. One of them is that time-consuming finalizers delay the recycling of dead objects, possibly causing OutOfMemoryError.

 

Posted in java, JDK packages | Tagged: , , , , , , , , , , , , , , | Leave a Comment »