在 .NET 中固定内存对象的生命周期

Pinning memory in .NET the lifetime of an object

我最近了解到在 .NET 中固定并不是实际过程。 "just" 在 IL 中创建一个固定的局部变量,这个变量指向的所有内容都被 GC 认为是固定的。您可以阅读更多相关信息 here

现在我想知道:是否可以固定 classstruct 的字段,以便它指向的 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 时可以吗?或者 structclass 字段不能具有固定对象的效果吗? (也许只有局部变量才有可能?)

不是字段,不是。从本质上讲,您在这里绝对正确:

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];
}

显然,这带有所有常见的免责声明以及健康和安全警告。确保您了解风险。