none
Handle Leak in Thread class

    Question

  • I have noticed what appears to be a handle leak in the Thread class.  When I create a new Thread object 5 WIndow handles get created (1 thread and 4 event handles).  When I join the thread back to the main thread those handle objects don't get destroyed and since there is no Dispose method on the Thread object there doesn't seem to be any way to force the handles to be released other than waiting for the garbage collection to get get rid of them.  I am missing something or is this a bug in the Thread class?  Here is my sample code:

           private void _StartThread_Click(object sender, EventArgs e)

            {

                _Abort = false;

                _TestThread = new Thread(MyThread);

                _TestThread.Start();

                _StartThread.Enabled = false;

                _StopThread.Enabled = true;

            }

     

            private void _StopThread_Click(object sender, EventArgs e)

            {

                _Abort = true;

                _TestThread.Join();

                _StartThread.Enabled = true;

                _StopThread.Enabled = false;

     

            }

     

            void MyThread()

            {

                while (true)

                {

                    if (_Abort)

                    {

                        return;

                    }

                    Thread.Sleep(100);

                }

            }

    Monday, January 15, 2007 8:10 PM

Answers

  • I thought that non-memory resources such as handles should not rely on garbage collection to be freed.  Shouldn't the Thread class support Dispose so that handles could be freed without waiting for garbage collection?

     

    Monday, January 15, 2007 9:14 PM
  • Non-memory resources ultimately rely on a finalizer to get released, just in case the programmer forgets to call Dispose().  Yes, it certainly looks like the Thread class ought to implement Dispose().  The "By design" answer to the Product Feedback report suggests that there is some sort of problem implementing it for the Thread class.  Hard to tell, there was no explanation at all.  There usually is at least a small response.

    It is half-way believable considering that Thread heavily interacts with the CLR.  The fact that GC.Collect() readily solves the problem makes it less than believable.  Maybe they just forget to add the method in .NET 1.0 and don't want to fix the problem because of compatibility concerns.  Or suddenly adding it would be a case of "egg in the face".  Or it is not really a problem because normal garbage collection sweeps usually take care of it.  Let's sit back and wait for an MSFT employee to respond...
    Monday, January 15, 2007 10:34 PM
    Moderator

All replies

  • Yes, I see this too.  It allocates up to about 50,000 handles but then jumps back, suggesting that garbage collection is doing its job.  Forcing a garbage collection after each Join() keeps the number of handles bound at 160.

    Product Feedback has a report on this issue; closed as "By Design".
    Monday, January 15, 2007 8:59 PM
    Moderator
  • I thought that non-memory resources such as handles should not rely on garbage collection to be freed.  Shouldn't the Thread class support Dispose so that handles could be freed without waiting for garbage collection?

     

    Monday, January 15, 2007 9:14 PM
  • Non-memory resources ultimately rely on a finalizer to get released, just in case the programmer forgets to call Dispose().  Yes, it certainly looks like the Thread class ought to implement Dispose().  The "By design" answer to the Product Feedback report suggests that there is some sort of problem implementing it for the Thread class.  Hard to tell, there was no explanation at all.  There usually is at least a small response.

    It is half-way believable considering that Thread heavily interacts with the CLR.  The fact that GC.Collect() readily solves the problem makes it less than believable.  Maybe they just forget to add the method in .NET 1.0 and don't want to fix the problem because of compatibility concerns.  Or suddenly adding it would be a case of "egg in the face".  Or it is not really a problem because normal garbage collection sweeps usually take care of it.  Let's sit back and wait for an MSFT employee to respond...
    Monday, January 15, 2007 10:34 PM
    Moderator
  • I would still like to know if this is considered a bug that will be fixed in future versions or if this is really "By design".  I guess I need to wait for a response to this from an MSFT employee as you suggest...
    • Proposed as answer by Ed Nicholas Friday, April 10, 2009 11:04 PM
    Friday, January 19, 2007 1:56 AM
  • I wonder if you every got a response on this.  I reported a similar problem recently to Microsoft (see https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=430646).  They responded as follows:

    Thank you for the feedback. I am the threading developer on the Common Language Runtime (CLR) team. The problem you report is a known issue in the CLR, and we are working to fix it in a future release. The problem is that CLR thread handles (and other associated data structures) are cleaned up by the finalizer thread, which normally only runs in response to a garbage collection (GC). If many threads are created and destroyed before a GC occurs (which would be the case if there were few memory allocations in the meantime) then it has the effect of "leaking" handles and memory, althgough these will be reclaimed the next time finalization is triggered. A workaround is to periodically manually trigger finalization, through calls to GC.WaitForPendingFinalizers. As I said, we are working to correct this in a future CLR release.

    Another work-around I found was to use ThreadPool objects - but they don't give you the flexibility of Thread objects.

    Ed
    Friday, April 10, 2009 11:08 PM
  • I had a similar issue, but what fixed the leak was GC.Collect(2, GCCollectionMode.Forced).

    GC.WaitForPendingFinalizers did not help.

     

    This is with application that had many wpf third party components, events and ui updates that were happening every 5 seconds, including tree updates. And it was leaking handles.

    I verified that by cycling major logical parts, handle leak was not happening. But it happened when the whole flow was executed. When in debug mode, following the flow, leak does not happen. This was timing related. See more here.

     

    Wednesday, August 25, 2010 4:54 PM