1

我似乎在一些已经生产一段时间的代码中遇到了内存泄漏(我看到我的进程在 PerfMon 中的 Process\Private Bytes 计数器随着时间的推移而上升)。请注意,除了泄漏之外,代码可以正常工作。似乎负责的代码如下(如果我删除处理传递给非托管代码的委托的代码,泄漏就会消失):

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();
    }
    ...
}

....

测试代码只是在循环中创建和处置 MyClass 的实例。

所以基本上我们有一个委托,我们在类构造函数中传递给非托管代码,我们包装在一个 GCHandle 中以防止它被过早收集并在非托管代码尝试使用它时导致某种访问冲突。使用 WinDbg 并在进程开始之后比较堆的快照时,我可以看到一个正在上升。我最终追查到:

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

在我看来,当调用 GetFunctionPointerForDelegateInternal() 并且它没有被释放时正在发生分配,因此托管委托对象可能永远不会被垃圾收集,因此我的泄漏?但是我正在释放我的 GCHandle,所以我在这里缺少什么?

注意:我什至尝试清空我的非托管调用,所以基本上它什么都不做,并且泄漏仍然发生,这告诉我托管方面有问题。

更新:我接受了删除对 Marshal.GetFunctionPointerForDelegate() 的调用并更改我的 p/invoke 以直接传递委托并让编组器为我处理它的建议,但是泄漏仍然发生。

更新:我更新了 Dispose() 代码,我原来的帖子有一些错别字。

4

2 回答 2

2

只需使用委托参数而不是IntPtr. marshaler 会为你做所有这些事情。

于 2012-09-13T16:54:17.717 回答
0

据我所知,委托是 C# 中的函数指针列表。将它传递给 C++ 中的函数指针参数让我觉得会有问题。您应该考虑添加一个本机函数来报告状态并将这个函数汇集在 C# 中的单独线程或后台工作程序中。这样您就不再需要传递委托了。

于 2012-09-13T17:57:40.520 回答