Wednesday, August 25, 2010

Handling Leaking Handles

Problem:

image

If Handle count reaches more than 5000 count, whole PC might freeze and become unresponsive.

In the Event Log, you might have seen: “The server was unable to allocate from the system nonpaged pool because the pool was empty” message before system frozen.

 

This application 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.

 

Cause:

Short Story Shorter, this is how I fixed it. Calling GC.Collect(2, GCCollectionMode.Forced) on each cycle fixed the leak. Yes, you would say that eventually GC should collect the thread. Here is a relevant issue that other people reported. And Microsoft defect that had to do with how GC cleans up unused threads, this is what they say:

“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.“ –Microsoft.

 

Solution 1:

So if you are in a long run leaking handles, see if GC.Collect(2, GCCollectionMode.Forced)  fixes it. If not, then you deal with logical gaps. Readon further.

 

Solutions 2,3,4,..:

Possible Leak Causes:

 

What does NOT leak Handles:

  • Extra subscriptions to Events do not lead to leaking Handles
  • Dispatcher.BeginInvoke(DispatcherPriority.Normal, new ThreadStart((), does not leak handles. Verified.

 

Note:

Close or Dispose on StreamWriter related objects? In most cases, it is more appropriate to Dispose it instead of Close, it depends, see article Close vs. Dispose.

 

More about Handles.

1 comments:

rubenhak said...

Since it is a bug in Microsoft CLR you may expect any weird operation to be a workaround. Just like emptying the recycle bin to cleanup unused Handles.

However, I think when this bug gets fixed by Microsoft you should never call GC.Collect and GC.WaitForPendingFinalizers. The CLR should take care of unused objects and clean them up whenever is "the best" time to do it.

For now should just use the workaround :(. Also in the bug tracking system it was marked as fixed but didn't find in which version it got actually fixed.

Post a Comment