2
\$\begingroup\$

I am somewhat aware of the differences between classes and structs, when each is allocated on the heap vs the stack, what happens when calling methods etc.

As far as I know, the memory for a struct field in a class is located on the heap, since it is part of the class. What happens when the struct is replaced with a new instance though? Is the content in the area on the heap that the old instance pointed to simply replaced with the content of the new instance? Or is the memory of the old instance targetted for garbage collection and the new instance getting some fresh memory?

struct Vector2 { ... }
class Player
{
     public Vector2 Position;
     ...
}

void Main // Whatever
{
    var player = new Player();
    player.Position = new Vector2(); // <--- Will this cause garbage collection?
}
\$\endgroup\$
1
  • 1
    \$\begingroup\$ As struct is a value type I would imagine the compiler will be smart enough to optimize the generated IL to simply "overwrite" the old struct - but I'm not sure if that behavior is guaranteed. \$\endgroup\$ Jan 11, 2017 at 12:07

3 Answers 3

2
\$\begingroup\$

No, inspecting disassembly, correct me if I am wrong, appears to construct it in-place(compiled by VS2015 compiler) - lea loading the address of the struct and movq the value there

            var vec = new Vector2();//for comparison
lea         edi,[ebp-4Ch]  //address of
xorps       xmm0,xmm0  //zeroing
movq        mmword ptr [edi],xmm0  //moving result to edi address
            player.Position = new Vector2();
mov         eax,dword ptr [ebp-44h]  
cmp         al,byte ptr [eax+4]  
lea         edi,[eax+4]  //address of
xorps       xmm0,xmm0  //zeroing
movq        mmword ptr [edi],xmm0  //moving result to edi address
\$\endgroup\$
1
  • \$\begingroup\$ I wish I could correct you. My assembler skills are... non-existent. \$\endgroup\$ Jan 11, 2017 at 13:38
4
\$\begingroup\$

Simple Answer

This isn't properly precise, but it's "good enough" for most day-to-day work:

Value types like structs do not themselves create garbage collection overhead.

If you already have some memory set aside for a struct somewhere, assigning a new value to it doesn't give the compiler or runtime any cause to go reserve memory elsewhere - it already has the space it needs to store the value.

Contrary to a comment above, the new keyword applied to value types in C# does not mean there's a heap allocation (ie. potential garbage generation) happening. In this context, new just means "invoke the constructor method to decide how to populate this variable." So these two lines are equivalent:

int myInt = new int();
int myInt = 0;

In both cases, it's the int myInt part that said "I want somewhere to store an integer," and the two different conclusions just sort out what value to stick in there. (They'd likely both compile to the constant 0 anyway).

More Complicated Answer

This section is based on a pair of in-depth discussions by Eric Lippert.

Despite how often it's claimed "structs are allocated on the stack" (even in Microsoft's own documentation), "stack" is an implementation detail of C# on desktop - there's nothing in the language spec that says every implementation must allocate structs on the stack. And in fact there are cases where structs get allocated on the heap:

  • If the struct is a member of a class, then it will be stored in the class. This doesn't create additional GC allocations beyond the one for the class instance itself.

  • If the struct gets "boxed" (eg. if you use Unity's SendMessage(string methodName, object value) method to send it as payload, that object value instance is a reference type and gets allocated on the heap). Here it's the boxing that's creating garbage collection entries.

  • If the struct is captured in a lambda or anonymous method, or part of an iterator block (you can think of this like the first point - we're creating an instance of an object that can have a lifetime independent of the current scope, so we set aside some memory for it, eg. the iterator object, and the struct it needs gets pulled along for the ride, but isn't itself creating another garbage collection entry beyond the one for the iterator instance)

So what's the real difference with value types like structs? Copy versus reference semantics.

myType foo = newFoo();
foo.baz = 0;

myType bar = foo;
bar.baz = 1;

Did this code create one new myType instance, so references foo and bar both point at this instance (ie. now foo.baz == 1)? It did if myType is a reference type. That means the myType instance needs to exist in memory at least as long as the lifetime of every reference to it, which is where garbage collection comes in, to handle these types of "long-lived" storage needs.

Or, did this code create two new myType instances, and copy the value of foo into bar (ie. it retains foo.baz == 0). It did if myType is a value type. That means that as soon as foo or bar go out of scope, we can safely let the data they were referencing expire, since there aren't any other references to it. (Even any methods using the data with a ref modifier would have themselves exited first - the semantics won't let us give the data to something with an independent lifetime like a Coroutine or Thread without copying it to them) The compiler & runtime can reliably treat these values as "short-lived" and avoid creating extra garbage tracking overhead for them.

\$\endgroup\$
3
\$\begingroup\$

No, that line will not have any impact on the garbage collector. You are right in that the struct lives within the block of heap memory allocated for the class; the garbage collector only cares about tracking the class.

The "new" keyword here is a red-herring; using new on a struct never does a heap allocation or impacts the garbage collector in any way (unless of course the constructor of the struct news up some reference types (classes) of its own).

\$\endgroup\$
1
  • \$\begingroup\$ Given that the size of a struct is know it makes sense to re-use that heap memory when changing the struct. "Make sense" does not equal "implementation" though. Thanks. \$\endgroup\$ Jan 11, 2017 at 13:30

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .