Thread Objects are never Garbage Collected
I found something interesting the other day. If you run the following program under NetBeans 5.5 using the memory profiler you can see that Thread objects (and their subclasses) never get garbage collected. You have to wait for some of the threads to stop and manually invoke the garbage collector.
I created a bug-report on this since I think it's pretty basic that the runtime should garbage collect Thread objects after the run() method has returned.
I ran into this why trying to track down an elusive memory leak in our software.
Am I missing something? Why has this not been reported before?
publicclass Threadsextends Thread
{
public Threads()
{
}
publicvoid run()
{
System.out.println(getName() +" - starting");
try
{
sleep((int) (Math.random() * 100000));
}
catch (InterruptedException e)
{
}
System.out.println(getName() +" - stopping");
}
publicstaticvoid main(String [] arguments)
{
for (int i = 0; i < 100; i++)
{
Threads threads =new Threads();
threads.start();
try
{
Thread.sleep(2000);
}
catch (InterruptedException e)
{
}
}
System.exit(0);
}
}
# 1
When you run the memory profiler, what do you observe that tells you thatThread objects are never garbage collected?
# 2
> I think it's pretty basic that the runtime should garbage collect Thread objects
> after the run() method has returned.
No. It's pretty basic, as you say, that those Thread objects should be subject to garbage collection. But you seem to be assuming that garbage collection will always clean up every single unreferenced object. That just isn't a goal of garbage collection. Its goal is to ensure you have enough memory to allocate another object. So if you have 25 megabytes of free memory, it isn't going to scrounge every last object so you can have 25.03 megabytes.
# 3
> When you run the memory profiler, what do you observe
> that tells you that Thread objects are never garbage collected?
After letting the program run for a while, until some of the threads have stopped, I explicitly invoke the garbage collector several times. From the memory profile I can see that the number of Threads objects alive always equals the number created, and that the Threads objects have survived each garbage collection generation.
# 4
> No. It's pretty basic, as you say, that those Thread
> objects should be subject to garbage
> collection. But you seem to be assuming that
> garbage collection will always clean up every single
> unreferenced object. That just isn't a goal of
> garbage collection. Its goal is to ensure you have
> enough memory to allocate another object. So if you
> have 25 megabytes of free memory, it isn't going to
> scrounge every last object so you can have 25.03
> megabytes.
In my real application I can see objects being garbage collected each time I explicitly invoke the garbage collector, but I never see a Thread (or subclass of Thread) object ever being collected.
I'm still open to the idea I'm really insane and that something else - perhalps normal - is going on.
# 5
I reported this problem to Sun back in December and got back (Incident Review ID: 865192). A few days later I got a request for more information and sent them some. Since then Sun have become a black hole and repeated attempts to clarify the problem go unanswered.
In the bug database I noticed many reports of unborn threads not being garbage collected. Has anyone else noticed any problems with thread objects not being garbage collected?
# 6
Yippee - Sun finally got back to me. If you think this is an important bug to fix, please vote on it.
Hi Eric Kolotyluk,
I am sorry for the delayed response. Due to the large number of queries we receive, we were unable to act as promptly as usual.
Thanks for the additional information. This will be really be useful as we do an indepth analysis of the problem.
We have determined that this report is a new bug and entered the bug into our internal bug tracking system under Bug Id: 6520834.
You can monitor this bug on the Java Bug Database at
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6520834.
It may take a day or two before your bug shows up in this external database. As you are a member of the Sun Developer Network (SDN), there are two additional options once the bug is visible.
1. Voting for the bug
Click http://bugs.sun.com/bugdatabase/addVote.do?bug_id=6520834.
2. Adding the report to your Bug Watch list.
You will receive an email notification when this bug is updated.
Click http://bugs.sun.com/bugdatabase/addBugWatch.do?bug_id=6520834.
# 7
I modified the Threads program to remove the calls to sleep and increase
the number of threads created. It basically just allocates ThreadCollect objects
(I also renamed the class for my convenience) and lets them die. I also
added periodic System.gc() calls to get some full collections. I ran it with the following
command line.
java -server -XX:+UseSerialGC -Xmx6m -Xms6m -XX:SurvivorRatio=1 -XX:NewRatio=1 -XX:+PrintGCDetails ThreadCollect
I also ran it with UseParallelGC. I ran the program until I saw 10-20 full collections
from the System.gc(). I looked for sustained growth in the live data
in the heap and did not see any. I think this experiment strongly suggests that
Thread Objects are being garbage collected.
public class ThreadCollect extends Thread
{
public ThreadCollect()
{
}
public void run()
{
return;
}
public static void main(String [] arguments)
{
for (int i = 0; i < 1000000000; i++)
{
ThreadCollect threads = new ThreadCollect();
threads.start();
if (i%1000000 == 0) {
System.gc();
}
}
System.exit(0);
}
}
# 8
Thanks for investigating this Jon. This leads me to wonder:
1) Is there something fundamentally flawed in the test I did? Why can I see some objects being collected in under the NetBeans profiler, but not Thread objects? Did I not run long enough or something? Is the NetBeans profiler somehow causing the Thread objects to not be collected?
2) Is there something special about the way you ran your test? For example, you're using the server runtime and I'm not. Does your test run the same on the client runtime?
I'll have to play around a little bit more, but at least you've given me more to think about.
Our commercial application still runs out of memory after running long enough. I'm not saying there aren't other memory leaks, it's just that this one has been the most consistent with the results I'm seeing.
# 9
A simple test, forever creating, starting and joining threads indicates that they are not leaked. Add a finalize method and you will see it being executed, so they are being GC'ed (or at least finalized).
I suspect the profiler is doing something to keep references to the threads itself. Presumably you can ask the profiler for the time used per-thread which means it needs to keep the threads ?
As for your application all I can suggest is using the heap analysis tool to examine what objects are being kept alive.
# 10
I ran ThreadCollect with -client and also did not see growth in the
heap. If you try David's suggestion about adding a finalizer,
use your original program (do not use ThreadCollect).The finalizer
thread needs time to execute the finalizer method and the rate
at which ThreadCollect creates threads could overwhelm the finalizer
thread.
# 11
I suggest going to a NetBeans forum/newsgroup to ask about the profiler behaviour.Meanwhile use a heap analysis tool on your real app to see where retention is occurring.As far as there base JDK/VM is concerned there is no indication of any problem.
# 12
Thanks guys for all the useful suggestions. I especially like the idea of writing finalizers to see if they are being called - I wish I'd thought of that.
It's kind of scary to think the NetBeans profiler can give you misleading information, after all the reason I use it is to look for memory leaks.
# 13
OK, I added a finalize method to my test class and a System.gc() inside my main loop. When I run the program normally, without the profiler, everything works fine - I can see the finalizers being called.
However, when I run the program under the profiler, the finalize method on the Thread object is never called.
I guess it's time to bug the NetBeans people.
Thanks again everyone.
# 14
> I guess it's time to bug the NetBeans people.
I already did that, submitting NetBeans Profiler issue # 101221, which was immediately flagged as a duplicate of 95020, which is fixed in NetBeans Profiler 5.5.1 RC. The annoying thing is I did 4-5 searches to see if the bug had already been submitted, but obviously didn't pick the right words. :-)
I closed the java.lang bug.
Regards,
Pete Soper
Petea at 2007-7-7 17:09:04 >
