How does the CLR (.NET) internally allocate and pass around custom value types (structs)?

Posted by stakx on Stack Overflow See other posts from Stack Overflow or by stakx
Published on 2010-05-15T13:37:09Z Indexed on 2010/05/15 13:44 UTC
Read the original article Hit count: 161

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?

© Stack Overflow or respective owner

Related posts about .NET

Related posts about clr