在 .NET 中固定内存对象的生命周期
Pinning memory in .NET the lifetime of an object
我最近了解到在 .NET 中固定并不是实际过程。 "just" 在 IL 中创建一个固定的局部变量,这个变量指向的所有内容都被 GC 认为是固定的。您可以阅读更多相关信息 here。
现在我想知道:是否可以固定 class
或 struct
的字段,以便它指向的 object
被假定为由 GC 固定而不使用 GCHandle
左右。像这样 (伪代码!):
public unsafe [class|struct] Something
{
public byte[] Data = new byte[4096];
private /*some keywords*/ byte* ptr = /*some keywords like fixed*/ Data;
}
如果这在普通 C# 中是不可能的,那么在使用 IL 时可以吗?或者 struct
或 class
字段不能具有固定对象的效果吗? (也许只有局部变量才有可能?)
不是字段,不是。从本质上讲,您在这里绝对正确:
Maybe it's only possible for local variables?
是的,只能用于局部变量。
这里的重点是GC不希望爬堆来寻找pin(很高兴查看堆栈-它已经需要这样做),并且没有对象的概念自己选择置顶
您当然可以使用固定 local 来实现此目的:
fixed(byte* ptr = obj.Data)
{
RunYourMainCode(obj);
}
但这需要固定本地跨越需要保留固定方法的代码。
如果你真的想让东西不动而且你不能使用本地:
- 使用
GCHandle
(用于),或者
- 使用非托管内存
请注意,使用 Memory<T>
和 Span<T>
,您仍然可以使用 托管 代码(即几乎为零 unsafe
使用)与非托管 内存。具体来说,可以在不安全的内存上构造 Memory<T>
,.Span
从中提供 ref T
对数据的访问(ref T
是托管指针,与 T*
这是一个非托管指针;非常相似,但托管指针与 GC 一起工作并且不需要 unsafe
).
在 .NET 5 中:
class SoMuchFastArray
{
static readonly int[] PinnedArray = GC.AllocateArray<int>(20, true);
unsafe static readonly int* ArrayPtr;
unsafe static SoMuchFastArray()
{
fixed (int* ptr = PinnedArray) { ArrayPtr = ptr; } // leak the pointer
}
unsafe int Wow(int index) => ArrayPtr[index];
int NotSoWow(int index) => PinnedArray[index];
}
显然,这带有所有常见的免责声明以及健康和安全警告。确保您了解风险。
我最近了解到在 .NET 中固定并不是实际过程。 "just" 在 IL 中创建一个固定的局部变量,这个变量指向的所有内容都被 GC 认为是固定的。您可以阅读更多相关信息 here。
现在我想知道:是否可以固定 class
或 struct
的字段,以便它指向的 object
被假定为由 GC 固定而不使用 GCHandle
左右。像这样 (伪代码!):
public unsafe [class|struct] Something
{
public byte[] Data = new byte[4096];
private /*some keywords*/ byte* ptr = /*some keywords like fixed*/ Data;
}
如果这在普通 C# 中是不可能的,那么在使用 IL 时可以吗?或者 struct
或 class
字段不能具有固定对象的效果吗? (也许只有局部变量才有可能?)
不是字段,不是。从本质上讲,您在这里绝对正确:
Maybe it's only possible for local variables?
是的,只能用于局部变量。
这里的重点是GC不希望爬堆来寻找pin(很高兴查看堆栈-它已经需要这样做),并且没有对象的概念自己选择置顶
您当然可以使用固定 local 来实现此目的:
fixed(byte* ptr = obj.Data)
{
RunYourMainCode(obj);
}
但这需要固定本地跨越需要保留固定方法的代码。
如果你真的想让东西不动而且你不能使用本地:
- 使用
GCHandle
(用于),或者 - 使用非托管内存
请注意,使用 Memory<T>
和 Span<T>
,您仍然可以使用 托管 代码(即几乎为零 unsafe
使用)与非托管 内存。具体来说,可以在不安全的内存上构造 Memory<T>
,.Span
从中提供 ref T
对数据的访问(ref T
是托管指针,与 T*
这是一个非托管指针;非常相似,但托管指针与 GC 一起工作并且不需要 unsafe
).
在 .NET 5 中:
class SoMuchFastArray
{
static readonly int[] PinnedArray = GC.AllocateArray<int>(20, true);
unsafe static readonly int* ArrayPtr;
unsafe static SoMuchFastArray()
{
fixed (int* ptr = PinnedArray) { ArrayPtr = ptr; } // leak the pointer
}
unsafe int Wow(int index) => ArrayPtr[index];
int NotSoWow(int index) => PinnedArray[index];
}
显然,这带有所有常见的免责声明以及健康和安全警告。确保您了解风险。