.net - 如何将 Marshal.AllocHGlobal 分配的内存清零?

标签 .net memory unmanaged-memory

我正在通过 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 在目标结构上)。我想尽可能高效地将这些悬挂字段归零。

就上下文而言,此代码正在反序列化从 Linux 上的 C++ 发送的高频多播消息。

这是一个失败的测试用例:

// 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's suggestionunsafe 和使用 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 来将字节转换为目标类型。

最佳答案

[DllImport("kernel32.dll")]
static extern void RtlZeroMemory(IntPtr dst, UIntPtr length);
...
RtlZeroMemory(targetBytes, typeSize);

关于.net - 如何将 Marshal.AllocHGlobal 分配的内存清零?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1486999/

相关文章:

在 std::string 上使用 malloc/realloc/free 时出现 C++ 内存错误

c# - 在 C# 中有效使用指向任意内存位置的原始指针

c# - 解释 NetCore 上 C# TcpClient/TcpListener 的奇怪行为

c# - 从 WebBrowser 控件复制

c# - 私有(private)类成员中关键字 'this' 的正确用法是什么?

c++ - C++ 中最快的数组初始化

c# - .NET Framework 4 与 .NET Framework 3.5 中的 WCF 有哪些新功能?

Java消耗太多内存

c# - 如何在非托管内存中实例化 C# 类? (可能的?)