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

Mat*_*ias 5 .net c# garbage-collection

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

现在我想知道:是否可以固定 aclassstructso的字段,object使其指向的字段假定为由 GC 固定而不使用GCHandle左右。像这样(伪代码!)

public unsafe [class|struct] Something
{
    public byte[] Data = new byte[4096];
    private /*some keywords*/ byte* ptr = /*some keywords like fixed*/ Data;
}
Run Code Online (Sandbox Code Playgroud)

如果这在普通 C# 中是不可能的,那么在使用 IL 时是否可能?还是不能structclass字段有固定对象的效果?(也许只有局部变量才有可能?)

Mar*_*ell 5

不是作为一个领域,不。基本上,你在这里是绝对正确的:

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

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

这里的要点是 GC 不想必须爬行堆来找到 pin(很高兴查看堆栈 - 它已经需要这样做),并且没有对象本身选择成为的概念钉住。

您当然可以使用固定本地来实现此目的:

fixed(byte* ptr = obj.Data)
{
    RunYourMainCode(obj);
}
Run Code Online (Sandbox Code Playgroud)

但这需要固定的本地跨越需要方法保持固定的代码。

如果你真的想要一些东西不动并且你不能使用本地

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

请注意,使用Memory<T>Span<T>,您仍然可以使用托管代码(即几乎零unsafe使用)来与非托管内存通信。具体来说, aMemory<T>可以在不安全的内存上构造,并且.Spanfrom 提供ref T对数据的访问(ref T是托管指针,T*与之相反的是非托管指针;非常相似,但托管指针与 GC 一起工作,不需要unsafe)。

  • 我会跳到这里,附上关于即将推出的[新的*固定堆*和新的`GC.AllocateArray` API]的旁注(https://github.com/dotnet/coreclr/blob/master/Documentation/design-docs/ PinnedHeap.md) 允许创建已经固定的数组 - OP 正在尝试做的事情。 (2认同)

Mat*_*ins 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];
}
Run Code Online (Sandbox Code Playgroud)

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

  • 谢谢您的回答。我其实不知道现在是否应该投票。然而,你的方法名称让我笑了。 (3认同)
  • 不客气!无论如何,我都会支持你的评论!:) (3认同)