Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-4533087

If start method is never called on a thread, that thread is leaked

XMLWordPrintable

    • b28
    • generic, x86, sparc
    • generic, linux_redhat_7.2, solaris_7, windows_nt, windows_2000

      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)

            mmcclosksunw Michael Mccloskey (Inactive)
            mmcclosksunw Michael Mccloskey (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: