views:

219

answers:

6

In my understanding a singleton object will destroy only when the application is about to terminate. So in C++ I write a Singleton class to log my application and in that Singleton logger's destructor I log the time when my application was terminated. Things worked perfectly in C++.


Now I want to have that same logger in Java, as in java there is no destructor so I implemented the finalize method for that singleton logger. But it seem that finalize method actually never get called. So, I add that System.runFinalizersOnExit(true); line, somewhere in my code (though I know it is deprecated) and that finalize method get called every time before termination of the app. But still there is a problem! If I try to write anything on file in that finalize method, It does not work, though System.out work without any problem! :(

Can you guys help me on this problem? Here is a sample code of what I am try to do:

Singleton Logger Class:

public class MyLogger {
    FileWriter writer;
    private MyLogger() {
        try {
            this.writer = new FileWriter("log.txt");
        }
        catch (IOException ex) {
        }
    }

    public static MyLogger getInstance() {
        return MyLoggerHolder.INSTANCE;
    }

    private static class MyLoggerHolder {
        private static final MyLogger INSTANCE = new MyLogger();
    }

    @Override
    protected void finalize () {
        try {
            super.finalize();
            System.out.println("Here"); //worked correctly.
            this.writer.write(new Date().toString()+System.getProperty("line.separator"));
            this.writer.write("End");
            this.writer.flush(); //does not work!
            this.writer.close();
        }
        catch (Throwable ex) {
        }
    }
    public synchronized void log(String str) {
        try {
            this.writer.write(new Date().toString()+System.getProperty("line.separator"));
            this.writer.write(str+"\n");
            this.writer.flush();
        }
        catch (IOException ex) {
        }
    }
 }

Main:

public class Main {
    public static void main(String[] args) {
        System.runFinalizersOnExit(true);
        MyLogger logger = MyLogger.getInstance();
        logger.log("test");
    }
}
+1  A: 

Josh Bloch wrote in his book "Effective Java" that it is a bad practice to do anything in finalizers.

Your option is to do this manually whenever you close the application:

  • if you close it (with System.exit(0)), call that code there
  • if the user closes it, add a listener and call it there.
Bozho
+2  A: 

Effective Java 2nd Edition: Item 7: Avoid finalizers

Finalizers are unpredictable, often dangerous, and generally unnecessary. Their use can cause erratic behavior, poor performance, and portability problems. Finalizers have few valid uses, [...] but as a rule of thumb, you should avoid finalizers.

[...] The only methods that claim to guarantee finalization are System.runFinalizersOnExit and its evil twin, Runtime.runFinalizersOnExit. These methods are fatally flawed and have been deprecated.

polygenelubricants
+1  A: 

As the other answers say, using finalize is considered bad practice. The "official" way to have something happen when the VM shuts down is to use a shutdown hook.

A shutdown hook is a Thread object that you create, but don't start. You register it with Runtime.getRuntime().addShutdownHook(Thread) and it will be called when the VM shuts down.

mdma
+1 - Just be aware that there are ways that a JVM can terminate **without** the shutdown hooks running. For example, a JVM crash or a "kill -9" will do it, as will a hardware failure, a power failure or a meteorite strike.
Stephen C
A: 

Besides that fact that using finalize is not safe (as I'm sure you've seen this since the method you are using System.runFinalizersOnExit is deprecated) your finalize method may be throwing an exception/error. In this case your exception/error is caught in your catch statement and nothing is done about it. A better way to do this would be:

try{
   System.out.println("Here"); //worked correctly.
   this.writer.write(new Date().toString()+System.getProperty("line.separator"));
   this.writer.write("End");
   this.writer.flush(); //does not work!
   this.writer.close();
}
catch(Throwable e){
   //Log or handle the issue
}
finally{
   super.finalize();
}

Again with all this said, using finalize in Java is generally not a good practice and should be avoided.

brainimus
A: 

I'd suggest that instead of trying to roll your own logging framework, you check out one of the many pre-existing Java logging frameworks. I use log4j, but there's plenty of options! You'll get more stable code, less debugging work, and probably faster performance. And, they don't need tricky finalizer behaviors.

Steven Schlansker
+1  A: 

This is one of those curious questions. Note: I wouldn't write my own Logging class - look at log4j or something...

As already stated, I really wouldn't really use finalize.

...

From what you say, your aim is just to run something as the program stops.

I'd register a shutdownhook instead ...

A shutdownhook is a user-defined object which extends Thread class. e.g. you could define a static inner class inside your logger

Here's an example:

http://www.crazysquirrel.com/computing/java/basics/java-shutdown-hooks.jspx

And here's the API doc: http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Runtime.html#addShutdownHook%28java.lang.Thread%29

amir75
Looks like I was a bit slow. Someone already said it ..
amir75
Thanks :DI just get it done without any finalize! Someone already said it, but you gave the link, which was more helpful.Thanks to you and to everyone :)
sowrov