Is there a way to temporarily store managed pointers in a stackalloc buffer without incurring unexpected behavior from the GC? #106653
-
I'm working with C# in a game engine and frequently need to pass around subsets of collections of objects to different synchronous functions. I'd like to avoid making heap allocations for each call, and have investigated a few solutions. One that interested me and seemed elegant conceptually was to stackalloc a buffer and store references to the objects in it, then pass that buffer as a Span to whatever function I needed to call. However, I know the docs state that stackalloc can only be used with unmanaged types. I've read a few descriptions of why this is the case, including this SO answer. My limited understanding is that the garbage collector needs to track all of the potential references to objects for the purposes of moving and freeing them. But stackalloc creates a value type with no fields, just a raw blob of memory with no type info, so the GC inherently can't know anything about it. Through use of the Unsafe and MemoryMarshal classes, I've written a workaround that allocates and reinterprets a buffer on the stack: IntPtr* ptr = stackalloc IntPtr[count];
ref T tRef = ref Unsafe.AsRef<T>(ptr); // T is some object
Span<T> buff = MemoryMarshal.CreateSpan(ref tRef, count);
... // Copy references into buff and pass and call function with it, then deallocate and return This works as expected so far. However, I haven't used this solution extensively yet and don't know if there will be any unexpected behavior over sustained GC runs. I know the objects won't be freed as they're still referenced by the original heap allocated collections. But I want to ensure the objects won't be moved by the GC while calling a function with these stack buffers. Is this possible? Will the GC account for the references stored in this memory because it's now being pointed to by a Span? If not, is it possible to avoid the referenced objects from being moved using the Span with the fixed statement and pinning them? If that won't work, is there any other way to ensure they won't be moved for the duration of the function call, or is this just not possible whatsoever? |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 14 replies
-
You can try using GC.Collect() and GC.WaitForFullGCComplete(). In this example, you can see that the string references were collected.
|
Beta Was this translation helpful? Give feedback.
-
There is nothing wrong with just storing gc references on stack (not sure why
If you don't want them to be moved, you have to pin every object in your buffer, otherwise GC may change pointers in your buffer while native code is doing something with them (see e.g. |
Beta Was this translation helpful? Give feedback.
There is nothing wrong with just storing gc references on stack (not sure why
stackalloc
doesn't support managed types - there are proposals like #33960 and related links) - you can define a struct with gc fields and put it on stack.If you don't want them to be moved, you have to pin every object in your buffer, otherwise GC may change pointers in your buffer while native code is doing something with them (see e.g.
GCHandle
)