Other |
---|
5.0 b28Fixed |
Duplicate :
|
|
Duplicate :
|
|
Duplicate :
|
|
Duplicate :
|
|
Duplicate :
|
|
Duplicate :
|
|
Duplicate :
|
|
Relates :
|
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)
|