Does passing Reference Types using ref save memory?

Mister Smith picture Mister Smith · Aug 24, 2011 · Viewed 6.9k times · Source

In C#, the parameters to a method can be either reference types or value types. When passing reference types, a copy of the reference is passed. This way, if inside a method we try to reassign the passed reference to another object instance, outside of the method the reassignment is not visible.

To make this working, C# has the ref modifier. Passing a reference type with ref actually uses the original reference instead of a copy. (Correct me if I'm wrong).

In this case, since we are not creating a copy of the reference, are we saving any memory? If a method is extensively called, does this improve the overall performance of the application?

Thanks!

Answer

user541686 picture user541686 · Aug 24, 2011

Claim

No, it doesn't. If anything, it's slower because of the extra lookup.

There's no reason to pass a reference type by reference unless you specifically intend to assign to it later.


Proof

Since some people seem to think that the compiler passes "the variable itself", take a look at the disassembly of this code:

using System;

static class Program
{
    static void Test(ref object o) { GC.KeepAlive(o); }

    static void Main(string[] args)
    {
        object temp = args;
        Test(ref temp);
    }
}

which is (on x86, for simplicity):

// Main():
// Set up the stack
00000000  push        ebp                    // Save the base pointer
00000001  mov         ebp,esp                // Set up stack pointer
00000003  sub         esp,8                  // Reserve space for local variables
00000006  xor         eax,eax                // Zero out the EAX register

// Copy the object reference to the local variable `temp` (I /think/)
00000008  mov         dword ptr [ebp-4],eax  // Copy its content to memory (temp)
0000000b  mov         dword ptr [ebp-8],ecx  // Copy ECX (where'd it come from??)
0000000e  cmp         dword ptr ds:[00318D5Ch],0  // Compare this against zero
00000015  je          0000001C               // Jump if it was null (?)
00000017  call        6F910029               // (Calls some internal method, idk)

// THIS is where our code finally starts running
0000001c  mov         eax,dword ptr [ebp-8]  // Copy the reference to register
0000001f  mov         dword ptr [ebp-4],eax  // ** COPY it AGAIN to memory
00000022  lea         ecx,[ebp-4]            // ** Take the ADDRESS of the copy
00000025  call        dword ptr ds:[00319734h] // Call the method

// We're done with the call
0000002b  nop                                // Do nothing (breakpoint helper)
0000002c  mov         esp,ebp                // Restore stack
0000002e  pop         ebp                    // Epilogue
0000002f  ret                                // Return

This was from an optimized compilation of the code. Clearly, there's an address of a variable being passed, and not "the variable itself".