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

7

最近我了解到,在.NET中进行固定处理并不是一个实际的过程。它只是在IL中创建一个固定的局部变量,而所有这个变量指向的东西都被GC视为固定状态。您可以在此处了解更多信息。

现在我想知道:是否有可能针对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时是否可以实现?或者 struct 或 class 字段不能使对象固定?(也许只有对于局部变量才可能实现?)
2个回答

7

不是作为字段,这是正确的。本质上,您在这里完全正确:

也许只有对于局部变量才可能?

是的,它只适用于局部变量。

关键在于GC不想必须爬取堆来查找引用(它很高兴查看堆栈-它已经需要这样做),并且没有概念可以使对象自己选择固定。

当然,您可以使用固定的局部变量来实现这一点:

fixed(byte* ptr = obj.Data)
{
    RunYourMainCode(obj);
}

但是这需要固定的本地变量跨越需要保留固定方法的代码。

如果您真的想让某些内容不移动,并且不能使用本地变量

  • 使用GCHandle(这就是它的作用),或者
  • 使用非托管内存

请注意,使用Memory<T>Span<T>,您仍然可以使用托管代码(即几乎零的unsafe用法)来访问非托管内存。具体而言,可以在不安全的内存上构建Memory<T>,从中提供.Span对数据进行ref T访问(ref T是托管指针,与T*形成对比,后者是非托管指针;非常相似,但托管指针可与GC一起工作,不需要unsafe)。


4
我想在这里提一下一个即将到来的新的Pinned Heap和一个新的GC.AllocateArray API,它允许创建已经固定的数组 - 这是OP试图做的事情。 - Konrad Kokosa
该存储库已于2023年1月23日被所有者归档。现在已变为只读状态。更新后的链接似乎是https://github.com/dotnet/runtime/blob/main/docs/design/features/PinnedHeap.md。 - Per Lundberg

5
在.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];
}

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


3
谢谢你的回答。实际上,我不确定现在是否应该给它点赞。不过,你的方法名称让我笑了。 - Matthias
3
不用谢!无论如何我都会给你的评论点赞! :) - Matt Jenkins

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接