views:

917

answers:

3

(Jeopardy-style question, I wish the answer had been online when I had this issue)

Using Java 1.4, I have a method that I want to run as a thread some of the time, but not at others. So I declared it as a subclass of Thread, then either called start() or run() depending on what I needed.

But I found that my program would leak memory over time. What am I doing wrong?

+27  A: 

This is a known bug in Java 1.4: http://bugs.sun.com/bugdatabase/view_bug.do;jsessionid=5869e03fee226ffffffffc40d4fa881a86e3:WuuT?bug_id=4533087

It's fixed in Java 1.5 but Sun doesn't intend to fix it in 1.4.

The issue is that at construction time, a Thread is added to a list of references in an internal thread table. It won't get removed from that list until its start() method has completed. As long as that reference is there, it won't get garbage collected.

So, never create a thread unless you're definitely going to call its start() method. A Thread object's run() method should not be called directly.

A better way to code it is to implement the Runnable interface rather than subclass Thread. When you don't need a thread, call

myRunnable.run().

When you do need a thread:

myThread = new Thread(myRunnable);
myThread.start();
slim
Is this an implementation dependent issue (say, of Sun's JVM), or is it outlined somewhere in the Java Language Specification?
Alexander
Edited - I did some more investigation and it's a JVM bug
slim
+1  A: 

Let's see if we could get nearer to the core of the problem:

If you start your program (lets say) 1000 x using start(), then 1000 x using run() in a thread, do both loose memory? If so, then your algorithm should be checked (i.e. for outer objects such as Vectors used in your Runnable).

If there is no such memory leak as described above then you should investigate about starting parameters and memory usage of threads regarding the JVM.

Georgi
+2  A: 

I doubt that constructing an instance of a Thread or a subclass thereof leaks memory. Firstly, there's nothing of the sorts mentioned in the Javadocs or the Java Language Specification. Secondly, I ran a simple test and it also shows that no memory is leaked (at least not on Sun's JDK 1.5.0_05 on 32-bit x86 Linux 2.6):

public final class Test {
  public static final void main(String[] params) throws Exception {
    final Runtime rt = Runtime.getRuntime();
    long i = 0;
    while(true) {
      new MyThread().run();
      i++;
      if ((i % 100) == 0) {
        System.out.println((i / 100) + ": " + (rt.freeMemory() / 1024 / 1024) + " " + (rt.totalMemory() / 1024 / 1024));
      }
    }
  }

  static class MyThread extends Thread {
    private final byte[] tmp = new byte[10 * 1024 * 1024];

    public void run() {
      System.out.print(".");
    }
  }
}

EDIT: Just to summarize the idea of the test above. Every instance of the MyThread subclass of a Thread references its own 10 MB array. If instances of MyThread weren't garbage-collected, the JVM would run out of memory pretty quickly. However, running the test code shows that the JVM is using a small constant amount of memory regardless of the number of MyThreads constructed so far. I claim this is because instances of MyThread are garbage-collected.

Alexander
Aha - did some more research and found out that it's a bug that got fixed in Java 1.5
slim