如何归零通过Marshal.AllocHGlobal分配的内存?
-
18-09-2019 - |
题
我在分配通过Marshal.AllocHGlobal
我的应用程序的一些非托管内存。然后我复制的一组字节到该位置,并通过再次struct
释放内存之前所得到的存储器段转换为Marshal.FreeHGlobal
。
这里的方法:
public static T Deserialize<T>(byte[] messageBytes, int start, int length)
where T : struct
{
if (start + length > messageBytes.Length)
throw new ArgumentOutOfRangeException();
int typeSize = Marshal.SizeOf(typeof(T));
int bytesToCopy = Math.Min(typeSize, length);
IntPtr targetBytes = Marshal.AllocHGlobal(typeSize);
Marshal.Copy(messageBytes, start, targetBytes, bytesToCopy);
if (length < typeSize)
{
// Zero out additional bytes at the end of the struct
}
T item = (T)Marshal.PtrToStructure(targetBytes, typeof(T));
Marshal.FreeHGlobal(targetBytes);
return item;
}
这适用于在大多数情况下,然而,如果我有更少的字节比所述struct
的尺寸要求,则“随机”值被分配给最后一个字段(我使用对目标结构LayoutKind.Sequential
)。我想尽可能有效归零这些挂字段越好。
有关上下文中,该码被反序列化从C发送高频多播消息++ Linux上。
下面是一个失败的测试用例:
// Give only one byte, which is too few for the struct
var s3 = MessageSerializer.Deserialize<S3>(new[] { (byte)0x21 });
Assert.AreEqual(0x21, s3.Byte);
Assert.AreEqual(0x0000, s3.Int); // hanging field should be zero, but isn't
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
private struct S3
{
public byte Byte;
public int Int;
}
运行该测试重复导致所述第二断言每次失败,并为不同的值。
修改强>
在结束时,我用 leppie的要去unsafe
和使用stackalloc
建议。这个分配的字节数组根据需要这是归零,改进的吞吐量为50%和100%之间,这取决于该消息的尺寸(较大的消息看到更大的好处)。
最后一个方法结束了,类似于:
public static T Deserialize<T>(byte[] messageBytes, int startIndex, int length)
where T : struct
{
if (length <= 0)
throw new ArgumentOutOfRangeException("length", length, "Must be greater than zero.");
if (startIndex < 0)
throw new ArgumentOutOfRangeException("startIndex", startIndex, "Must be greater than or equal to zero.");
if (startIndex + length > messageBytes.Length)
throw new ArgumentOutOfRangeException("length", length, "startIndex + length must be <= messageBytes.Length");
int typeSize = Marshal.SizeOf(typeof(T));
unsafe
{
byte* basePtr = stackalloc byte[typeSize];
byte* b = basePtr;
int end = startIndex + Math.Min(length, typeSize);
for (int srcPos = startIndex; srcPos < end; srcPos++)
*b++ = messageBytes[srcPos];
return (T)Marshal.PtrToStructure(new IntPtr(basePtr), typeof(T));
}
}
不幸的是这仍然需要调用Marshal.PtrToStructure
到字节转换成目标类型。
解决方案
为什么不检查start + length
是否typesize
内?
BTW:我只想去unsafe
这里使用一个for循环来零出额外的内存
这也将让您使用stackalloc
这是更安全,比AllocGlobal
更快的利益。
其他提示
[DllImport("kernel32.dll")]
static extern void RtlZeroMemory(IntPtr dst, int length);
...
RtlZeroMemory(targetBytes, typeSize);
这将正常工作在Windows上:
namespace KernelPInvoke
{
/// <summary>
/// Implements some of the C functions declared in string.h
/// </summary>
public static class MemoryWrapper
{
[DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)]
static extern void CopyMemory(IntPtr destination, IntPtr source, uint length);
[DllImport("kernel32.dll", EntryPoint = "MoveMemory", SetLastError = false)]
static extern void MoveMemory(IntPtr destination, IntPtr source, uint length);
[DllImport("kernel32.dll", EntryPoint = "RtlFillMemory", SetLastError = false)]
static extern void FillMemory(IntPtr destination, uint length, byte fill);
}
var ptr = Marshal.AllocHGlobal(size);
try
{
MemoryWrapper.FillMemory(ptr, size, 0);
// further work...
}
finally
{
Marshal.FreeHGlobal(ptr);
}
}
是如乔恩赛格尔说,可以使用零Marshal.WriteByte它
在下面的例子中,我复制结构之前零出缓冲器。
if (start + length > messageBytes.Length)
throw new ArgumentOutOfRangeException();
int typeSize = Marshal.SizeOf(typeof(T));
int bytesToCopy = Math.Min(typeSize, length);
IntPtr targetBytes = Marshal.AllocHGlobal(typeSize);
//zero out buffer
for(int i=0; i < typeSize; i++)
{
Marshal.WriteByte(targetBytes, i, 0);
}
Marshal.Copy(messageBytes, start, targetBytes, bytesToCopy);
我从来没有做过这样的东西在C#中,但我发现Marshal.WriteByte(IntPtr的,的Int32,字节)在MSDN。尝试了这一点。
for(int i=0; i < buffSize / 8; i += 8 )
{
Marshal.WriteInt64(buffer, i, 0x00);
}
for(int i= buffSize % 8 ; i < -1 ; i-- )
{
Marshal.WriteByte (buffer, buffSize - i, 0x00);
}
我想你会发现它是快好几倍使用64个莱特,而不是8个赖特(你还需要在最后几个字节)。
我想零出一个缓冲的最佳方法是这样的,如果你不想,还是不能走另外一条道路:
for(int i=0; i<buffSize; i++)
{
Marshal.WriteByte(buffer, i, 0x00);
}