2012-09-13 32 views
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()的代碼,我原來的職位有一些錯別字。

+2

由於您正在使用windbg,因此可以使用dumpheap -stat來查看累積的對象類型。此外,你在做什麼這個rigmarole?只需將您的本地方法聲明爲接受委託。封送員會爲你做所有這些事情。 –

+0

看起來它使用COM來提供編組(每個'GetFunctionPointerForDelegateInternal'的源代碼),所以當你完成它的時候,你可以在'IntPtr'上調用'Marshal.Release()'。 – user7116

+0

我更新了p/inovke以簡單地使用委託,但泄漏仍然存在。使用!dumpheap -stat在運行一段時間之後,我可以看到幾十萬個委託對象。這似乎證實了代表們正在積累。 – JosephA

回答

2

只需要聲明你用一委託參數,而不是IntPtr本地方法。封送員會爲你做所有這些事情。

+0

我改變了我的本地方法的聲明中使用委託和刪除我Marshal.GetFunctionPointerForDelegate()的用法,但我的泄漏仍然發生,我仍然可以再次向下跟蹤WinDbg中泄漏到CLR!MarshalNative :: GetFunctionPointerForDelegateInternal。 – JosephA

+1

您也不需要爲委託分配GCHandle。只保留一個正常的引用就足以防止它被垃圾回收,並且如果你的本地調用沒有將委託存儲在非託管內存的某個地方供以後使用(例如異步回調),即使這是不必要的。 –

0

據我所知代表是C#中函數指針的列表。將它傳遞給C++中的函數指針參數會讓我覺得會有問題。您應該考慮添加一個本地函數來報告狀態,並將其放入C#中的單獨線程或後臺工作器中。這樣你就不需要再傳遞一個委託了。