Saturday, March 27, 2010

Exception Handling


Exception handling is one of the good features of Java and C#, which is highly abused. The following are the questions that one should ask, while working with exceptions.

If you are catching an exception, and throwing it without taking any action, why do you need to catch an exception in the first place?

If you are opening a resource, and if you must close the resource once it is done, how do you make sure that, the resources are always closed?

The following is the best way to handle this.


open connection
try {
Use connection
} finally {
close connection
}


Let's suppose, if you have opened two resources, then many people try to do like below.


Declare Resource1
Declare Resource2
try {
Open Resource1
Open Resource2
Use both the resources
} finally {
if(Resource1 != null)
Close Resource1;
if(Resource2 != null)
Close Resource2;
}


There is a problem in the above code. If closing of Resource1 throws any exception, then Resource2 will not be closed. The better way to do this is,


Open Resource1
try {
Open Resource2
try {
Use both the resources
} finally {
Close Resource2
}
} finally {
Close Resource1;
}


If you have multiple resources, then the nesting level will increase. But, that is not a problem. Having single try finally block gives problems in case of failures. For any general database query, there would be three try finally blocks (For Connection, Statement and ResultSet). It is always a best practice to have try finally blocks for each resource you are opening.

If you are developing a library, which can be used by many others.

If you are getting an unexpected exception from another library, why do you want to catch it? Why not throw it without catching?

For example, you are developing a library to return some data from the database. Let's suppose, you are trying to open a database connection from a configuration file, and it throws an exception. Why do you want to handle it? Why not throw it to the upper layer, and let it handle. If you are planning to catch it, log it, and rethrow it, are you sure that, it is fine with all the developers who use that library. If all the components are logging essentially the same error, won't the logging become cluttered. Few developers who use that library, may not want any logging from that library. If you are planning to catch it, and return some error code, don't you think significant information is lost while doing that? Can you come up with all the error cases while connecting to a database? It is possible that, the error is because of not having the configuration file, having the invalid database name, database server, user name, password, or not having access to the db, or it can be anything. Why do you want to capture all those in your code? Let the upper layer decide on what to do. Let the exception go to the upper layer.

If you just have try finally block, and without any catch block, people tend to ask, why are you not catching any exception in the try finally block. Ask the above questions, and see whether you really need to catch an exception or not. If you are just catching the exception and rethrowing it, it is of no use.

If you are developing UI

Are you handling all the exceptions? It is not the case that, you should have catch(Exception) in all the UI pages. If something unexpected happens, is there anyway of knowing what is the exact error and stacktrace. It does not matter, whether you display the error in the page itself, or in a log file or event viewer. If you don't have any way of knowing, how would you debug it? You may not always have access to the production machine, and you may not be able to attach a debugger in all the cases.

14 comments:

  1. Use smart pointers for automatic closing in case of unexpected exits. Else, you have unnececessary constraints on your logic.

    ReplyDelete
  2. The way it works is, after you create a connection, have the reference of your your connection in an object specifically designed to store the reference. In the destructor of this new object, close the connection. Since this new object is local to the function where the connection is being created, in case of exception the compiler make sure the destructor is automatically called.

    ReplyDelete
  3. Java does not have destructor. The invocation of finalize() is not guaranteed to be called at any particular time.

    ReplyDelete
  4. Hmmm. In C++, destructor (finalize()) is called when the local object is going out of scope of the current functions, whether it is due to an exception or a graceful return.

    ReplyDelete
  5. "If an object has a finalizer, the finalizer method is invoked sometime after the object becomes unused (or unreachable), but before the garbage collector reclaims the object."

    http://docstore.mik.ua/orelly/java-ent/jnut/ch03_03.htm

    Are you sure "finalize" is not called just before the garbage collection of the local variables in the stack memory?

    ReplyDelete
  6. JVM calls finalize before it is garbage collected. But, not when we want. If you are opening database or network connections, you may never want to wait till the garbage collection is run.

    finalize would be useful only when the object is not so important.

    ReplyDelete
  7. Let us consider first code:

    Declare Resource1
    Declare Resource2
    try {
    1. Open Resource1
    2. Open Resource2
    3. Use both the resources
    4. Close Resource1;
    5. Close Resource2;
    }
    catch (all errors) {

    }

    In the above flow, any exception in any of the steps 1-5 is caught below. Just before entering the catch block, garbage collection is done on all the local objects since they all go out of scope, and hence, "finalize" too is called on all the local objects in the try block.

    ReplyDelete
  8. While closing Resource1, if there is any exception, then Resource2 will not be closed. We have to close all the resources irrespective of whether there are any exceptions anywhere else or not.

    It is guaranteed that, finalize would be called when garbage collection is run. But, when will be Garbage collection run is not in our control. JVM determines when to run garbage collection depending on the memory available. If garbage collection is not run for a long time, and if there are many unused opened connections, then the application may not get any new connection.

    For example, you are opening a database connection, and that database can open upto 20 connections at max. You opened 20 connections, and they were not closed, because the garbage collection is not run. In this case, you cannot open a new database connection till garbage collection is run. You may not want to wait for garbage collection to run for your database connection.

    ReplyDelete
  9. The timing of garbage collection is not random. JVM definitely does it immediately after exiting the try block, whether due to an exception or normal exit. Do you agree with this point?

    ReplyDelete
  10. It is not guaranteed that JVM calls garbage collection immediately after exiting the try block.

    There are many JVMs by many vendors. Each vendor has different implementation.

    The latest garbage collectors are more intelligent, and when they run, they don't run for the whole memory, but does only for a subset.

    ReplyDelete
  11. I see the problem now. However, there is a fix for this. In the catch block, we can force the garbage collector to act, with a call to System.gc();
    I do not know if/how proper it is. Thanks for the patient replies.

    ReplyDelete
  12. It is not a good idea to call System.gc() everywhere. The advantage of Java is, we don't need to worry about memory management. If we are calling System.gc() regularly, it means we are worried about memory every time. If our main problem is closing the connection, then it is better to close the connection rather than calling garbage collection which will close the connection at the time of cleaning up.

    Not only that, System.gc() is not guaranteed to free all the memory. It varies a lot from JVM to JVM. In Sun JVM, if you are calling System.gc() frequently, then from second time onwards, it won't clean up completely. Sometimes, it may not free anything.

    ReplyDelete
  13. How many times do exceptions occur? The call to system.gc(), I mentioned, is in the try block of the exception, not in the catch block.

    The second point you raised is more serious.

    ReplyDelete
  14. Exceptions occur, whenever there is an problem. That mainly depends on your program, environment, and problems in that.

    We should try to solve the problem in clean and efficient way. Calling System.gc() does not solve the problems always, even if it solves, it is very inefficient.

    ReplyDelete