JDK-4533087 : If start method is never called on a thread, that thread is leaked
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang
  • Affected Version:
    1.1.7,1.2.1,1.3.0,1.3.1,1.4.0,1.4.1,1.4.2,1.4.2_04 1.1.7,1.2.1,1.3.0,1.3.1,1.4.0,1.4.1,1.4.2,1.4.2_04
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS:
    generic,linux_redhat_7.2,solaris_7,windows_nt,windows_2000 generic,linux_redhat_7.2,solaris_7,windows_nt,windows_2000
  • CPU: generic,x86,sparc
  • Submitted: 2001-11-29
  • Updated: 2004-02-10
  • Resolved: 2003-11-11
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
5.0 b28Fixed
Related Reports
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Relates :  
Description
This occurs in all versions of the JDK, from 1.1.5 through 1.2.
If you create a thread and never call its start() method,
then later null your reference to that thread, it will
never be garbage collected.  The reason is that the 
java.lang.Thread class adds the thread to the thread
group in the init() method, which is called from the
constructor.  It removes the thread from the thread
group in the exit() method, which is only called if the
thread is run.  So if the thread is never run, the thread
group still has a reference to the Thread and it will never
be garbage collected.  And of course any objects which the
thread has references to will in turn not be garbage collected.

The following code illustrates this.  

                                                          
class ThreadLeak {

  static MyThread t;
    
  public static void main(String[] args) {

    if ((args.length > 0) && args[0].equals("run")) {    
      System.out.println("Creating and running 10,000 threads...");
      for(int i = 0; i < 10000; i++) {
        t = new MyThread(true);        
      }
    } else {
      System.out.println("Creating 10,000 threads, but not running them...");
      for(int i = 0; i < 10000; i++) {
        t = new MyThread(false);        
      }
    }
    
    System.out.println("Running garbage collector...");    
    System.gc();

    System.out.println("Done.  Heap size is " + 
                       (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()));            
  }
}

class MyThread implements Runnable {

  Thread t;
    
  public MyThread(boolean bRun) {
    t = new Thread(this, "MyThread");
    if (bRun) {
      t.start();
    }    
  }
  
  public void run() {
    /* NO-OP */
  }        
}

This creates 10,000 threads and optionally runs them.
If you run this code with "java ThreadLeak", the threads are
created but not run, and you will see that the used heap 
memory is well over 1MB.  If you run this code with
"java ThreadLeak run", the threads are created and run,
and you will see that the heap is much smaller because
the threads have not been leaked.  This is a rather contrived
example for simplicity, but in reality even one leaked thread
can cause a large memory leak if it has references to other
objects.  

How to fix this is debatable.  If you wait until the 
start() method to add the thread to the thread group, then
any call to the thread's enumerate() method will not list
the thread until it is run.  This might be an acceptable quirk.
Or you could add a new public API to remove the thread from the 
the thread group in the even that it is never used
(currently, ThreadGroup.remove() is a private method).  
Or at the very least, document this behavior so people can avoid
the memory leak by waiting until the thread needs to be run
before creating it.
(Review ID: 48015)

Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: tiger-beta FIXED IN: tiger-beta INTEGRATED IN: tiger-b28 tiger-beta
14-06-2004

EVALUATION Could be fixed in libraries with WeakReferences, although there are also VM issues involved. ###@###.### 2002-02-08 We will not add a thread to its threadgroup until it has been started. ###@###.### 2003-10-21 Decommiting this bug from 1.4.2_05 list as it is risky to backport this to an update release. Also there is no active customer escalations on this bug. ###@###.### 2004-02-10
10-02-2004