Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pinning memory in .NET the lifetime of an object

I recently learned that pinning in .NET is no actual process. It's "just" creating a pinned local variable in IL and everything this variable is pointing to is considered pinned by the GC. You can read more about this here.

Now I wonder: Is it possible to pin a field of a class or struct so that the object it points to is assumed as pinned by the GC without using GCHandle or so. Something like this (pseudocode!):

public unsafe [class|struct] Something
{
    public byte[] Data = new byte[4096];
    private /*some keywords*/ byte* ptr = /*some keywords like fixed*/ Data;
}

If this is not possible within plain C#, is it possible when using IL? Or can't struct or class fields have the effect of pinning objects? (Maybe it's only possible for local variables?)

like image 939
Matthias Avatar asked Sep 11 '19 08:09

Matthias


2 Answers

Not as a field, no. Essentially, you're absolutely correct here:

Maybe it's only possible for local variables?

Yes, it is only possible for local variables.

The point here is that the GC does not want to have to crawl the heap to find pins (it is happy to look at the stack - it already needs to do that), and there is no concept of an object by itself electing to be pinned.

You can of course use a pinned local to achieve this:

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

but this requires the pinned local to span the code that needs the method to retain pinned.

If you really want something to not move and you can't use a local:

  • use a GCHandle (that's what it is for), or
  • use unmanaged memory

Note that with Memory<T> and Span<T>, you can still use managed code (i.e. almost zero unsafe usage) to talk to unmanaged memory. Specifically, a Memory<T> can be constructed over unsafe memory, and the .Span from that provides ref T access to the data (ref T is a managed pointer, contrast to T* which is an unmanaged pointer; very similar, but managed pointers work with GC and do not require unsafe).

like image 58
Marc Gravell Avatar answered Sep 21 '22 06:09

Marc Gravell


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

Obviously, this comes with all the usual disclaimers and health and safety warnings. Make sure you understand the risks.

like image 21
Matt Jenkins Avatar answered Sep 23 '22 06:09

Matt Jenkins