Question:
Do all CLR value types, including
user-defined structs, live on the
evaluation stack exclusively, meaning
that they will never need to be
reclaimed by the garbage-collector, or
are there cases where they are
garbage-collected?
Background:
I have previously asked a question on SO about the impact that a fluent interface has on the runtime performance of a .NET application. I was particuarly worried that creating a large number of very short-lived temporary objects would negatively affect runtime performance through more frequent garbage-collection.
Now it has occured to me that if I declared those temporary objects' types as struct (ie. as user-defined value types) instead of class, the garbage collector might not be involved at all if it turns out that all value types live exclusively on the evaluation stack.
What I've found out so far:
I did a brief experiment to see what the differences are in the CIL generated for user-defined value types and reference types. This is my C# code:
struct SomeValueType { public int X; }
class SomeReferenceType { public int X; }
.
.
static void TryValueType(SomeValueType vt) { ... }
static void TryReferenceType(SomeReferenceType rt) { ... }
.
.
var vt = new SomeValueType { X = 1 };
var rt = new SomeReferenceType { X = 2 };
TryValueType(vt);
TryReferenceType(rt);
And this is the CIL generated for the last four lines of code:
.locals init
(
[0] valuetype SomeValueType vt,
[1] class SomeReferenceType rt,
[2] valuetype SomeValueType <>g__initLocal0, //
[3] class SomeReferenceType <>g__initLocal1, // why are these generated?
[4] valuetype SomeValueType CS$0$0000 //
)
L_0000: ldloca.s CS$0$0000
L_0002: initobj SomeValueType // no newobj required, instance already allocated
L_0008: ldloc.s CS$0$0000
L_000a: stloc.2
L_000b: ldloca.s <>g__initLocal0
L_000d: ldc.i4.1
L_000e: stfld int32 SomeValueType::X
L_0013: ldloc.2
L_0014: stloc.0
L_0015: newobj instance void SomeReferenceType::.ctor()
L_001a: stloc.3
L_001b: ldloc.3
L_001c: ldc.i4.2
L_001d: stfld int32 SomeReferenceType::X
L_0022: ldloc.3
L_0023: stloc.1
L_0024: ldloc.0
L_0025: call void Program::TryValueType(valuetype SomeValueType)
L_002a: ldloc.1
L_002b: call void Program::TryReferenceType(class SomeReferenceType)
What I cannot figure out from this code is this:
Where are all those local variables mentioned in the .locals block allocated? How are they allocated? How are they freed?
Why are so many anonymous local variables needed and copied to-and-fro only to initialize my two local variables rt and vt?