[英]Memory leaks? in passing IEnumerable<byte[]> array to unmanaged function as byte** parameter
這是分配和釋放對傳遞給非托管dll的托管數據的句柄的正確方法嗎?
有帶有導出功能的非托管dll
void Function(byte** ppData, int N);
我需要將它傳遞給IEnumerable<byte[]> afids
var handles = afids.Select(afid => GCHandle.Alloc(afid, GCHandleType.Pinned));
var ptrs = handles.Select(h => h.AddrOfPinnedObject());
IntPtr[] afidPtrs = ptrs.ToArray();
uint N = (uint)afidPtrs.Length;
Function(afidPtrs, N);
handles.ToList().ForEach(h => h.Free());
我收到托管內存泄漏,並且在“即時窗口”中獲取sos.dll導致gcroot
DOMAIN(00275030):HANDLE(Pinned):3ea2c0:Root: 17a8d190(System.Byte[])
函數定義為:
[DllImport("My.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
static extern unsafe internal int Function(IntPtr[] ppData, int N);
控制台應用程序的錯誤代碼段:
static void Main(string[] args)
{
while (!Console.KeyAvailable)
{
IEnumerable<byte[]> data = CreateEnumeration(100);
PinEntries(data);
Thread.Sleep(900);
Console.Write(String.Format("gc mem: {0}\r", GC.GetTotalMemory(true)));
}
}
static IEnumerable<byte[]> CreateEnumeration(int size)
{
Random random = new Random();
IList<byte[]> data = new List<byte[]>();
for (int i = 0; i < size; i++)
{
byte[] vector = new byte[12345];
random.NextBytes(vector);
data.Add(vector);
}
return data;
}
static void PinEntries(IEnumerable<byte[]> data)
{
var handles = data.Select(d => GCHandle.Alloc(d, GCHandleType.Pinned));
var ptrs = handles.Select(h => h.AddrOfPinnedObject());
IntPtr[] dataPtrs = ptrs.ToArray();
Thread.Sleep(100); // unmanaged function call taking byte** data
handles.ToList().ForEach(h => h.Free());
}
控制台應用程序的正確代碼段:
static void PinEntries(IEnumerable<byte[]> data)
{
IEnumerable<GCHandle> handles = CreateHandles(data);
IntPtr[] ptrs = GetAddrOfPinnedObjects(handles);
Thread.Sleep(100); // unmanaged function call taking byte** data
FreeHandles(handles);
}
static IEnumerable<GCHandle> CreateHandles(IEnumerable<byte[]> data)
{
IList<GCHandle> handles = new List<GCHandle>();
foreach (byte[] vector in data)
{
GCHandle handle = GCHandle.Alloc(vector, GCHandleType.Pinned);
handles.Add(handle);
}
return handles;
}
static IntPtr[] GetAddrOfPinnedObjects(IEnumerable<GCHandle> handles)
{
IntPtr[] ptrs = new IntPtr[handles.Count()];
for (int i = 0; i < ptrs.Length; i++)
ptrs[i] = handles.ElementAt(i).AddrOfPinnedObject();
return ptrs;
}
static void FreeHandles(IEnumerable<GCHandle> handles)
{
foreach (GCHandle handle in handles)
handle.Free();
}
static void PinEntries(IEnumerable<byte[]> data)
{
var handles = data.Select(d => GCHandle.Alloc(d, GCHandleType.Pinned));
var ptrs = handles.Select(h => h.AddrOfPinnedObject());
IntPtr[] dataPtrs = ptrs.ToArray();
Thread.Sleep(100); // unmanaged function call taking byte** data
handles.ToList().ForEach(h => h.Free());
}
您陷入了Linq陷阱,其枚舉器的行為不像集合。 句柄被分配兩次 ,第一次是在使用ptrs.ToArray()時,第二次是在使用handles.ToList()時。 具有明顯的副作用,即第一組手柄無法釋放。 固定:
var handles = data.Select(d => GCHandle.Alloc(d, GCHandleType.Pinned)).ToList();
var ptrs = handles.Select(h => h.AddrOfPinnedObject());
IntPtr[] dataPtrs = ptrs.ToArray();
handles.ForEach(h => h.Free());
請注意添加的ToList()強制將枚舉放入集合中。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.