in your case - string allocates on heap.
but reference types can be allocated on stack.
for exemplar, arrays can be allocated as:
private static void Main(string[] args) { unsafe { const int size = 123; var mgmtArray = new long[size]; mgmtArray[0] = 12345; void* refAddr = Unsafe.AsPointer(ref mgmtArray); long mgmtArrAddr = *(long*)refAddr; long mtAddr = *(long*)mgmtArrAddr; long arrSize = ((int*)mgmtArrAddr)[2]; Console.WriteLine("Addr ref: {0:x}", (long)refAddr); Console.WriteLine("Mgmt array addr: {0:x}", mgmtArrAddr); Console.WriteLine("MT: {0:X}", mtAddr); Console.WriteLine($"{nameof(arrSize)}: {arrSize}"); long* stackPtr = stackalloc long[size * 2]; stackPtr[0] = 0; //sync block stackPtr[1] = mtAddr; ((int*)stackPtr)[4] = size; stackPtr++; long** stackPtrRef = &stackPtr; var pointer = new MgmtPointer<long[]>(Unsafe.Read<long[]>(stackPtrRef)); long[] arrayOnStack = Unsafe.Read<long[]>(stackPtrRef); arrayOnStack[0] = 54321; Console.WriteLine("-----"); Console.WriteLine($"In heap: {mgmtArray[0]}"); //12345 Console.WriteLine($"In stack: {arrayOnStack[0]}"); //54321 Console.WriteLine($"arrayOnStack len: {arrayOnStack.Length}"); } }
for other reference types also, but with some changes.