I seem to be experiencing a memory leak in some code that's been in production for a while now (I see the Process\Private Bytes counter rising over time in PerfMon for my process). Note the code works without issue other than the leaking. The code that seems to be responsible is as follows (if I remove the code that handles the delegate passing into the unmanaged code the leak disappears):
private MyDelegate _myDelegate = null;
private GCHandle _myHandle;
public MyClass()
{
_myDelegate = new MyDelegate(target);
_myHandle = GCHandle.Alloc(_myDelegate);
if (NativeCalls.UnmanagedFunctionCall(_myDelegate))
{
...
}
}
public void Dispose()
{
GC.SuppressFinalize(this);
Dispose(true);
}
~MyClass()
{
Dispose(false);
}
protected void Dispose(bool disposing)
{
...
if (_myHandle.IsAllocated)
{
_myHandle.Free();
}
...
}
....
The test code simply creates and disposes of instances of MyClass in a loop.
So basically we have a delegate that we pass into unmanaged code in the class constructor, we wrap in a GCHandle to keep it from being collected too early and cause some kind of Access Violation when the unmanaged code tries to use it. Using WinDbg and comparing snapshots of the heap when the process begins to much later on I can see one rising. I eventually track it down to:
0:005> !heap -p -a 0bd779c8
address 0bd779c8 found in
_HEAP @ 400000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
0bd779a0 000c 0000 [00] 0bd779c8 00024 - (busy)
Trace: 09c3
59aba6a7 verifier!AVrfpDphNormalHeapAllocate+0x000000d7
59ab8f6e verifier!AVrfDebugPageHeapAllocate+0x0000030e
77260d06 ntdll!RtlDebugAllocateHeap+0x00000030
7721ae7e ntdll!RtlpAllocateHeap+0x000000c4
771c3cee ntdll!RtlAllocateHeap+0x0000023a
59accb62 verifier!AVrfpRtlAllocateHeap+0x00000092
7004691a clr!EEHeapAlloc+0x000000cb
70047f46 clr!CExecutionEngine::ClrHeapAlloc+0x0000004a
70047f15 clr!ClrHeapAlloc+0x00000023
7004fd84 clr!operator new+0x00000038
701392d1 clr!UMEntryThunk::CreateUMEntryThunk+0x0000000f
70139473 clr!MarshalNative::GetFunctionPointerForDelegateInternal+0x000000d6
6acd8448 mscorlib_ni+0x00238448
It seems to me that an allocation is occurring when GetFunctionPointerForDelegateInternal() is called and it's not getting freed so the managed delegate objects are maybe never getting garbage collected and hence my leak? But I am freeing my GCHandle so what am I missing here?
Note: I even tried emptying my unmanaged call, so basically it does nothing, and the leak still occurs so that tells me it's something wrong on the managed side.
Update: I took the suggestion of removing my call to Marshal.GetFunctionPointerForDelegate() and change my p/invoke to pass a delegate directly and let the marshaller handle it for me, however the leak still occurs.
Update: I updated the Dispose() code, my original post had some typos.
Just declare your native method with a delegate parameter instead of IntPtr
. The marshaler will do all this stuff for you.
As far as I know delegate is a list of function pointers in C#. Passing that to a parameter of function pointer in C++ makes me feels there will be a problem. You should consider adding a native function to report status and pool this one in a separate thread or background worker in C#. Such that you do not need to pass a delegate any more.
User contributions licensed under CC BY-SA 3.0