#926 – How Memory Is Allocated for Objects on the Managed Heap

When your applications starts up, the Garbage Collector (GC) allocates some virtual memory to use as a managed heap.  It creates a pointer to the next available free space in the managed heap, which defaults to point to the beginning of the (empty) heap.

When you use the new operator to create a new reference-typed object, the Garbage Collector allocates space on the managed heap for your object as follows:

  • It uses the pointer to the next available space on the heap to determine where to store your object
  • It advances the next available free space pointer, based on the size of your object
  • It zeroes out memory for the new object
  • It passes back a pointer to the new object, which is then stored in the reference-typed variable that refers to your object
  • The object’s constructor executes, to initialize data members within the object

 

Advertisement

#570 – Assignment Compatibility for Reference Types

A reference type is assignment compatible if the value being assigned belongs to a type that is either the same as, or is a derived type of, the type of the storage location being assigned to.  You can assign a variable of type T to a storage location of type U if T is a narrower type than U, or is the same type as U.

Hound huck = new Hound("Huckleberry", 55);

// Since Hound is a sub-class of Dog, we can
// assign to a variable of type Dog
Dog dog1 = huck;

In this case, the variable of type Dog will still be pointing to an instance of a Hound.  The assignment doesn’t change anything about the object.

To see this, construct an instance of a Dog directly and then compare the objects.

// This one points to an actual Dog instance
Dog dog2 = new Dog("Just some dog", 2);

#271 – Passing a Reference Type as an Output Parameter

You can pass a reference-typed variable to a method as an output parameter, using the out keyword.  This indicates that the parameter is an output method.  For a reference type, this means that the method is expected to change the reference to point to a different object.

        public static void FindBallPlayingDog(out Dog fav)
        {
            fav = null;
            foreach (Dog d in AllDogs)
            {
                if (d.Motto.Contains("ball"))
                    fav = d;
            }
        }

When calling the method, we need to include the out keyword as a parameter modifier:

            Dog kirby = new Dog("Kirby");
            kirby.Motto = "Love chasing balls";

            Dog jack = new Dog("Jack");
            jack.Motto = "Sneak away when possible";

            Dog fav = jack;

            Dog.FindBallPlayingDog(out fav);   // Fav is now Kirby

#270 – Passing a Reference Type by Reference

When you pass a reference type by value to a method, you pass in a reference to the object.  The method can change the object, but can’t change the reference.

        public static void AnnotateMotto(Dog d)
        {
            // Change the dog's motto
            d.Motto = d.Motto + " while panting";
        }

You can also pass a reference type by reference, which means that the method can change not only the contents of the object pointed to, but change the reference, causing it to refer to a different object.

        public static void FindNewFavorite(ref Dog fav)
        {
            fav.Motto = "No longer the favorite";

            foreach (Dog d in AllDogs)
            {
                if (d.Motto.Contains("ball"))
                    fav = d;
            }
        }

Below is an example of calling this method.

            Dog kirby = new Dog("Kirby");
            kirby.Motto = "Retrieve balls";

            Dog jack = new Dog("Jack");
            jack.Motto = "Growl at people";

            Dog myFav = jack;
            Dog.FindNewFavorite(ref myFav);

            Console.WriteLine(myFav.Name);   // Kirby

#266 – You Can’t Prevent a Method from Changing the Contents of Reference Types

We saw that even when you pass a variable by value to a method, if that variable is a reference to an object, the method can change the contents of the object.

C# doesn’t have a built-in language construct that allows you to tell the compiler to prohibit a method from changing the contents of an object that a parameter references.  (In C++, we can do this with the const keyword).

In C#, if you want to prevent a method from changing an object, you need to first clone your original object and pass a copy of the object to the method. (You’ll want to make a deep copy of the object).

Here’s an example of using the DeepCopy<T> method to pass a copy of the object.

            kirby.BarkAt(DeepCopy<Dog>(jack));


#265 – Passing Reference Types by Value

Recall that by default parameters are passed by value to a method.  A copy of the variable is passed to the method, which means that if the method changes the parameter’s value, the change won’t be seen by the calling code.

If a parameter’s type is a reference type, however, what is passed by value to the method is a reference to the object.  This means that while the method can’t change the reference, it can change aspects of the object that is referenced.

As an example, assume that we have a Dog.BarkAt method that takes a reference to another Dog.  In the code below, the BarkAt method actually changes the target dog’s BarkPhrase.

        public void BarkAt(Dog target)
        {
            Console.WriteLine("I say {0} to {1}", BarkPhrase, target.Name);

            // I can change target dog's properties
            target.BarkPhrase = "I-am-a-dope";
        }

This change is seen by the calling code.

#227 – Instances of Classes Are Created on the Heap

Because they are reference types, instances of classes are stored on the heap.  When you instantiate (or create) an instance of a class, the actual object is created on the heap and you reference the newly created object with a variable whose type corresponds to the class’ type.  The variable that references the object is stored on the stack.

            // New Person object created on the heap
            // p variable is a stack-based reference to the new object
            Person p = new Person("Cary", "Grant");

Because a class object is created on the heap, you don’t destroy the object explicitly, but it is automatically garbage collected.  The object will be a candidate for garbage collection when there are no longer any variables that reference the object.

            p = new Person("Jimmy", "Stewart");

            // No variable references Cary Grant anymore,
            // so he can be garbage-collected.

#218 – Store Value-Typed Objects on the Heap Through Boxing

Value-typed objects are typically stored on the stack, while reference-typed objects are stored on the heap.  You can, however, convert an instance of a value type to a reference type object through a process known as boxing.

In the example below, we box i, assigning it to a reference type variable.  Because i derives from System.Object (object), as do all types in .NET, we can assign i to o without doing a cast.  A new object is created on the heap, the value of i is copied into it, and the variable o is set to reference the new object.

            int i = 46;
            object o = i;   // Box i

Because a copy of the value-typed object is made, you can change the original object without changing the new object on the heap.

            i = 47;   // i now 47, but o still points to object with value of 46

#186 – Value Types on the Heap

Value types are normally allocated on the stack. They can, however, be allocated on the heap–when they are declared within an object.  When an object is instantiated, memory for the entire object, including all of its data, is allocated on the heap.

For example, assume we define a Person class with some properties that are value types (int) and some that are reference types (string).

    public class Person
    {
        string FirstName;
        string LastName;

        int Age;
        int HeightInInches;      // also on heap

        public Person(string firstName, string lastName)
        {
            FirstName = firstName;
            LastName = lastName;
        }
    }

When you create an instance of the Person object,

            Person p = new Person("Zsa Zsa", "Gabor");

the Person object is created on the heap and all of its data members, including the Age and HeightInInches value types, are also on the heap.

#67 – Default Behavior for Reference Type Equality

For reference types, a default equality check normally checks to see if the references are pointing to the exact same object–rather than checking to see if the objects pointed to are equivalent.

Suppose we have a Person type that has a constructor that takes Name and Age values.

 public class Person
 {
     public string Name { get; set; }
     public uint Age { get; set; }

     public Person(string name, uint age)
     {
         Name = name;
         Age = age;
     }
  }

Now suppose that we create two instances of the Person object with the same values for Name and Age.  The code fragment below shows what happens when we check to see if the resulting objects are equal.

 Person p1 = new Person("Sean", 46);
 Person p2 = new Person("Sean", 46);

 bool b = (p1 == p2);    // False, because p1 and p2 point to different objects