Library tutorials & articles

Memory in .NET - what goes where

Page 2 of 2
  1. Introduction
  2. So where are things stored?

So where are things stored?

So where are things stored?

The memory slot for a variable is stored on either the stack or the heap. It depends on the context in which it is declared:

  • Each local variable (ie one declared in a method) is stored on the stack. That includes reference type variables - the variable itself is on the stack, but remember that the value of a reference type variable is only a reference (or null ), not the object itself. Method parameters count as local variables too, but if they are declared with the ref modifier, they don't get their own slot, but share a slot with the variable used in the calling code. See my article on parameter passing for more details.
  • Instance variables for a reference type are always on the heap. That's where the object itself "lives".
  • Instance variables for a value type are stored in the same context as the variable that declares the value type. The memory slot for the instance effectively contains the slots for each field within the instance. That means (given the previous two points) that a struct variable declared within a method will always be on the stack, whereas a struct variable which is an instance field of a class will be on the heap.
  • Every static variable is stored on the heap, regardless of whether it's declared within a reference type or a value type. There is only one slot in total no matter how many instances are created. (There don't need to be any instances created for that one slot to exist though.) Note that this heap is separate from the normal garbage collected heap - it's known as a "high frequency heap", and there's one per application domain.

A worked example

The above may all sound a bit complicated, but a full example should make things a bit clearer. Here's a short program which does nothing useful, but should demonstrate the points raised above.

using System;
struct PairOfInts
{
    static int counter=0;
   
    public int a;
    public int b;
   
    internal PairOfInts (int x, int y)
    {
        a=x;
        b=y;
        counter++;
    }
}
class Test
{
    PairOfInts pair;
    string name;
   
    Test (PairOfInts p, string s, int x)
    {
        pair = p;
        name = s;
        pair.a += x;
    }
   
    static void Main()
    {
        PairOfInts z = new PairOfInts (1, 2);
        Test t1 = new Test(z, "first", 1);
        Test t2 = new Test(z, "second", 2);
        Test t3 = null;
        Test t4 = t1;
        // XXX
    }
}

Let's look at what's where in memory at the line marked with the comment "XXX". (Assume that nothing is being garbage collected.)

  • There's a PairOfInts instance on the stack, corresponding with variable z . Within that instance, a=1 and b=2 . (The 8 byte slot needed for z itself might then be represented in memory as 01 00 00 00 02 00 00 00 .)
  • There's a Test reference on the stack, corresponding with variable t1 . This reference refers to an instance on the heap, which occupies "something like" 20 bytes: 8 bytes of header information (which all heap objects have), 8 bytes for the PairOfInts instance, and 4 bytes for the string reference. (The "something like" is because the specification doesn't say how it has to be organised, or what size the header is, etc.) The value of the pair variable within that instance will have a=2 and b=2 (possibly represented in memory as 02 00 00 00 02 00 00 00 ). The value of the name variable within that instance will be a reference to a string object (which is also on the heap) and which (probably through other objects, such as a char array) represents the sequence of characters in the word "first".
  • There's a second Test reference on the stack, corresponding with variable t2 . This reference refers to a second instance on the heap, which is very similar to the one described above, but with a reference to a string representing "second" instead of "first", and with a value of pair where a=3 (as 2 has been added to the initial value 1). If PairOfInts were a reference type instead of a value type, there would only be one instance of it throughout the whole program, and just several references to the single instance, but as it is, there are several instances, each with different values inside.
  • There's a third Test reference on the stack, corresponding with variable t3 . This reference is null - it doesn't refer to any instance of Test . (There's some ambiguity about whether this counts as a Test reference or not - it doesn't make any difference though, really - I generally think of null as being a reference which doesn't refer to any object, rather than being an absence of a reference in the first place. The Java Language Specification gives quite nice terminology, saying that a reference is either null or a pointer to an object of the appropriate type.)
  • There's a fourth Test reference on the stack, corresponding with variable t4 . This reference refers to the same instance as t1 - ie the values of t1 and t4 are the same. Changing the value of one of these variables would not change the value of the other, but changing a value within the object they both refer to using one reference would make that change visible via the other reference. (For instance, if you set t1.name="third"; then examined t4.name , you'd find it referred to "third" as well.)

Comments

  1. 05 Jul 2005 at 10:32

    Whilst this posting is accurate, it does not go a whole long way to explaining the value of value types.  


    Consider the scenario of a large scale banking system, where system testing suddenly flips a very large number into floating point format (e.g. 1.6e20), and the Java developers say it is major change with performance implications to fix.. a daft statement from a business perspective...


    why is it a big changes: because Java does not have a value-type for big decimal numbers (BigDecimal is an object reference type).. and people commonly use double for currency because of the huge overhead (performance and coding) of using java.math.BigDecimal.


    struct exists in C# because there are a small number of scenarios where the language would not be appropriate/practical without them {Point (x,y), Complex, Decimal} are common examples of why value types that are essential.. so is there a down-side yes being value-types, they are passed by value (unless the “ref” parameter modifier is used) on the stack.. a Complex number is a good example, a vector of  3000 doubles is a poor example because it is copied every time (unless ref is used).


    A cool thing about C# is that you can decide later whether an object needs to be a value-type or not, but judicious use can make an application really fly because values can live of the stack

  2. 01 Jan 1999 at 00:00

    This thread is for discussions of Memory in .NET - what goes where.

Leave a comment

Sign in or Join us (it's free).

Jon Skeet C# MVP currently living in Reading and working for Google.

Related discussion

Related podcasts

  • More jQuery in ASP.NET

    In this episode Chris Brandsma, Rick Strahl, Dave Ward, Bertrand Le Roy, and Scott Koon conclude their discussion of Microsoft's jQuery in ASP.NET announcement1.This episode of the Alt.NET Podcast is brought to you by LLBLGen Pro, the most mature O/R mapper and code generator out there.Are ...

Events coming up

  • Dec 9

    GL.net Group Meeting - December 2009

    Gloucester, United Kingdom

    The beginning of this year holiday season will belong to mocks. Ronnie and Stephen will take us for a tour around exciting world of unit testing.

Want to stay in touch with what's going on? Follow us on twitter!