Reputation: 11
I have an application that very extensive use managed and unmanaged objects. There are memory leaks and I try to understand how to fix their. I used with SOS extension and run !objsize command. What HANDLE(RefCnt=0) does mean and how can I to check if object should be collected by GC?
DOMAIN(07E74830):HANDLE(RefCnt=1):9611400: sizeof(099290b8) = 1756 ( 0x6dc) bytes (BO.Account)
DOMAIN(07E74830):HANDLE(RefCnt=1):9611404: sizeof(09928a00) = 1764 ( 0x6e4) bytes (BO.Account)
DOMAIN(07E74830):c:9611408: sizeof(099237c8) = 25760 ( 0x64a0) bytes (BO.Account)
DOMAIN(07E74830):HANDLE(RefCnt=0):961140c: sizeof(099c7fc8) = 4084 ( 0xff4) bytes (BO.Account)
DOMAIN(07E74830):HANDLE(RefCnt=0):9611410: sizeof(099c9148) = 4084 ( 0xff4) bytes (BO.Account)
DOMAIN(07E74830):HANDLE(RefCnt=0):9611414: sizeof(099ca2c8) = 4084 ( 0xff4) bytes (BO.Account)
DOMAIN(07E74830):HANDLE(RefCnt=0):9611418: sizeof(099cb448) = 4084 ( 0xff4) bytes (BO.Account)
DOMAIN(07E74830):HANDLE(RefCnt=0):961141c: sizeof(099cc630) = 4312 ( 0x10d8) bytes (BO.Account)
Upvotes: 1
Views: 260
Reputation: 941675
They are garbage collection handles. Exposed in the .NET Framework with the GCHandle type. They keep a reference to a managed object, beyond the normal ones that the GC can discover by itself.
There are several different kinds and serve different purposes. The GCHandleType enum shows some of them, weak references and pinning are common uses. GCHandleType.Normal is common in unmanaged interop, it allows unmanaged code to keep a reference on a managed object, gcroot<>
is a wrapper for that.
And they are also used by the CLR, types that are not exposed by GCHandleType. The ones that show up in SOS output with "RefCnt" are a special kind that are used for COM interop, it keeps a reference to an RCW, Runtime Callable Wrapper, the managed wrapper object for a COM interface. These kind of handles are pretty notorious in Office interop, they cause the very visible side-effect of keeping the Office program running.
Seeing RefCnt=0 is not unusual and does not necessarily indicate a leak. It just means that the finalizer thread hasn't gotten around to calling the RCW finalizer yet. That takes time, the finalizer thread only runs after a garbage collection.
A common problem with managed code that relies heavily on COM interop is that it simply doesn't create enough garbage. So the garbage collector never (or rarely) kicks in and the finalizer thread doesn't get around to releasing the COM references. With the side effect that the program can use a lot of unmanaged memory, used by the unmanaged COM code. Worst case it can cause OOM. You diagnose this by looking at the GC performance counters with a profiler or Perfmon.exe. Make sure that gen#0 collections are occurring at a healthy clip. If that doesn't happen then the managed code needs to help by calling GC.Collect().
A not entirely uncommon problem is the finalizer thread deadlocking when trying to release a COM reference. The program will look like it is operating correctly but memory usage rapidly builds up. You diagnose this by looking at the finalizer thread with a debugger, you'll see it making the COM interop call and not progressing. This is caused by breaking the contract for an STA thread, it must pump a message loop.
Upvotes: 2