Beware The Unused Thread

Many people think that because Java has garbage collection, that memory leaks aren't possible – this is totally and utterly wrong. One really good way to introduce leaks into Java programs without blatantly holding on to object references is to create a new thread and then not use it. Once you create a thread, it is added to the list of runnable threads. I'm not sure why, but even before the thread is actually started, Java treats it as a running thread and holds on to it. Obviously threads that are currently running shouldn't be garbage collected, but it seems like an unfortunate side effect that newly created threads also can't be garbage collected. Once you know about it, the solution is simple – only ever create a thread immediately before you call its start method.

Today however, I learnt another unusual way to introduce memory leaks – start a javax.swing.Timer instance and don't keep a reference to it. The person who originally wrote this piece of code obviously thought that the Timer defaulted to firing once. It doesn't. Timer's default to spawning a new thread and continuing to fire the action until you call its stop method. This wouldn't be so bad if it weren't for the fact that the ActionListener passed to the timer was an internal class and thus brought along references to the rest of the system.

On the plus side, out of this ordeal I've managed to set up a simple way to profile our applet rather than just profiling the bean. The codebases only differ by one or two classes, but this memory leak was one that only appeared in the applet (and then only in a specific situation). I probably should document the pain I went through to get it working at some point, but not tonight.

2 Responses to “Beware The Unused Thread”

  1. Julius Davies Says:

    Adrian, can you help me out, I have one question, and one comment.

    #1. This is only a problem for while( true ) threads, right? Am I going to run into problems if just want to do a single small task in another thread? For example:

    // Obviously real-world example would read the response, log something, deal with exceptions…
    Runnable r = new Runnable() {
    public void run() {
    try {
    new URL( “http://mymonitor.com/its_alive?actor=me” ).openStream();
    } catch ( Exception e ) { }
    }
    };

    // Let’s attack!
    new Thread( r ).start();
    new Thread( r ).start();
    new Thread( r ).start();
    new Thread( r ).start();
    new Thread( r ).start();

    Are those 5 threads going to stick around even after the run() completes? That’s not intuitive to me! I have also monitored this kind of thing using kill -3, and they seemed to go away. (If they aren’t around in kill -3, are they still taking up memory!?!?!).

    #2. My comment is that while( true ) threads *really* do stay around. The Tomcat manager webapp ends up being useless to me in most circumstances. I can’t use stop/start or even undeploy/deploy against most webapps, because most start threads! (Or Timer’s, like you said). It wouldn’t be so bad if people would use the Servlet.destroy() hook to realize it’s time to stop all the threads their webapp created, but seems that’s quite rare.

    Even though the Tomcat manager webapp throws out the old classloaders for the undeploy, the classloader will stay around if some threads are still busy in their infinite loops. It gets really fun after a few months of undeploy/deploy when legions of old threads are fighting to read files, log events, read sockets!


  2. Adrian Sutton Says:

    Sorry, I should have been more clear – only Threads that are always running (while (true) as you point out) or threads that are never run (ie: never have their start() method called) will cause a memory leak. In your example the threads will be executed, exit their run method and be garbage collected just fine. The problem is only when you have:
    new Thread();
    and then don’t start() it. Threads that haven’t been started don’t turn up in kill -3 as far as I’m aware because they haven’t actually spawned a separate thread yet, but they do stay around.

    Threads in servlets are particularly nasty, in general they just shouldn’t be used and if they are, as you say, Servlet.destroy should stop them.


Leave a Reply

(Valid OpenIDs will skip moderation)

Alternatively, subscribe to the Atom feed.