Memory leak due to marshalling managed delegate?

1

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.

c#
c++
asked on Stack Overflow Sep 13, 2012 by JosephA • edited Sep 13, 2012 by JosephA

2 Answers

2

Just declare your native method with a delegate parameter instead of IntPtr. The marshaler will do all this stuff for you.

answered on Stack Overflow Sep 13, 2012 by Anton Tykhyy
0

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.

answered on Stack Overflow Sep 13, 2012 by Mahmoud Fayez

User contributions licensed under CC BY-SA 3.0