在固定块中创建实例会导致无效指针或损坏内存吗?
Can an instance creation in fixed block lead to invalid pointers or corrupt memory?
假设一个不安全的方法。有一个固定块通过第一个元素固定数组,即 fixed (void* p = &array[0])
。在该块中,实例正在通过 new
关键字或 Marshal.PtrToStructure
创建。我是否冒 p
指向无效地址的风险?这会破坏内存吗?
示例:
unsafe object ArrayToObject(Byte[] array, Type type)
{
// Precondition checks on arguments
fixed (void* p = &array[0])
{
return Marshal.PtrToStructure(new IntPtr(p), type));
}
}
我在上述实现中遇到了一个相当罕见的问题,即生成的对象与数组中的数据不匹配。 (最后一段要么全为 0,要么很明显它不可能是那样。)这个方法有时也会被频繁调用,通常相隔不到 1 毫秒。数组分配也在 单独的线程 上以这样的频率发生,所以这也可能导致我的问题。
我也没有以这种强度使用该软件,因此我无法真正重现该问题。此外,我目前只在.NET 4.0 上看到这个问题。
我最近读了一本书,建议避免固定块中的堆分配,但是这里没有看到任何风险提及,虽然看到很多保证对象已固定,不会移动。我还没有读完这本书,所以不确定这是否能解释这一点。 (至少不会立即解释这样的事情。)
最近还阅读了 GC 如何在第一代、第二代和第三代堆之间移动小对象,不幸的是 array
足够小(不超过 1,000 个元素),因此如果不是因为固定块。
目前我已经切换到 GCHandle
,我希望它能解决这个问题,但这让我很烦恼。
由于向后兼容性,我对 .NET 4.0 和 .NET 4.7 上的这种行为很感兴趣。
在 fixed
块中,p
定义得非常好,PtrToStructure
调用分配了一个 new 对象 从缓冲区中复制数据,所以:一旦PtrToStructure
完成,它来自哪里并不重要。因此,这段代码没有任何问题。但是,如果您试图在现有缓冲区内“分配”对象:这不是此代码的作用。
如果所涉及的类型是没有引用的 struct
(即满足 unmanaged
约束的类型),则有一些方法可以获得托管(即不是 unsafe
)引用数据 inside 现有数组,同时强制类型(通常在 byte
和一些自定义 struct
之间),例如:
static ref T Coerce<T>(byte[] array) where T : unmanaged
=> ref MemoryMarshal.Cast<byte, T>(array)[0];
使用情况:
static void Main()
{
byte[] data = new byte[16];
Console.WriteLine(BitConverter.ToString(data));
ref SomeStruct val = ref Coerce<SomeStruct>(data);
val.a = 1;
val.b = 2;
val.c = 3;
Console.WriteLine(BitConverter.ToString(data));
}
struct SomeStruct
{ // note: structs should usually be "readonly"; this
// is for convenience of example only
public int a, b, c;
}
请注意,通过将 Coerce<T>
签名中的 byte[]
更改为 Span<byte>
,这也可以用于任意内存(堆栈、非托管堆等)。
假设一个不安全的方法。有一个固定块通过第一个元素固定数组,即 fixed (void* p = &array[0])
。在该块中,实例正在通过 new
关键字或 Marshal.PtrToStructure
创建。我是否冒 p
指向无效地址的风险?这会破坏内存吗?
示例:
unsafe object ArrayToObject(Byte[] array, Type type)
{
// Precondition checks on arguments
fixed (void* p = &array[0])
{
return Marshal.PtrToStructure(new IntPtr(p), type));
}
}
我在上述实现中遇到了一个相当罕见的问题,即生成的对象与数组中的数据不匹配。 (最后一段要么全为 0,要么很明显它不可能是那样。)这个方法有时也会被频繁调用,通常相隔不到 1 毫秒。数组分配也在 单独的线程 上以这样的频率发生,所以这也可能导致我的问题。 我也没有以这种强度使用该软件,因此我无法真正重现该问题。此外,我目前只在.NET 4.0 上看到这个问题。
我最近读了一本书,建议避免固定块中的堆分配,但是这里没有看到任何风险提及,虽然看到很多保证对象已固定,不会移动。我还没有读完这本书,所以不确定这是否能解释这一点。 (至少不会立即解释这样的事情。)
最近还阅读了 GC 如何在第一代、第二代和第三代堆之间移动小对象,不幸的是 array
足够小(不超过 1,000 个元素),因此如果不是因为固定块。
目前我已经切换到 GCHandle
,我希望它能解决这个问题,但这让我很烦恼。
由于向后兼容性,我对 .NET 4.0 和 .NET 4.7 上的这种行为很感兴趣。
在 fixed
块中,p
定义得非常好,PtrToStructure
调用分配了一个 new 对象 从缓冲区中复制数据,所以:一旦PtrToStructure
完成,它来自哪里并不重要。因此,这段代码没有任何问题。但是,如果您试图在现有缓冲区内“分配”对象:这不是此代码的作用。
如果所涉及的类型是没有引用的 struct
(即满足 unmanaged
约束的类型),则有一些方法可以获得托管(即不是 unsafe
)引用数据 inside 现有数组,同时强制类型(通常在 byte
和一些自定义 struct
之间),例如:
static ref T Coerce<T>(byte[] array) where T : unmanaged
=> ref MemoryMarshal.Cast<byte, T>(array)[0];
使用情况:
static void Main()
{
byte[] data = new byte[16];
Console.WriteLine(BitConverter.ToString(data));
ref SomeStruct val = ref Coerce<SomeStruct>(data);
val.a = 1;
val.b = 2;
val.c = 3;
Console.WriteLine(BitConverter.ToString(data));
}
struct SomeStruct
{ // note: structs should usually be "readonly"; this
// is for convenience of example only
public int a, b, c;
}
请注意,通过将 Coerce<T>
签名中的 byte[]
更改为 Span<byte>
,这也可以用于任意内存(堆栈、非托管堆等)。