#930 – Objects on the Heap Can Refer to Each Other

When you set a reference-typed variable to refer to an object, either by creating a new object, or by referencing an existing object, you reference an object on the heap.  As long as the reference exists, the object will be allowed to exist on the managed heap and not get garbage collected.

In addition to an object being referenced by a reference-typed variable, objects may be referenced by other objects.  It may also be the case that an object is only referenced by another object and not by any reference-typed variable.  These objects will be kept alive only if they can be reached by some reference.

In the example below, the dogs HankLassie and Rex will all become eligible for collection because they are not reachable by any referenced-typed variable.  Dogs Kirby and Jack will be retained, because they are reachable.

930-001

#929 – Visualizing How Objects Are Removed from the Managed Heap

Suppose that you create two Dog objects as follows.  (The BestFriend property refers to another dog that is the first dog’s best friend).

            Dog myDog = new Dog("Kirby", 15);
            myDog.BestFriend = new Dog("Jack", 17);

The managed heap now looks as follows:

Untitled

Now suppose that you then execute the following lines of code:

            Dog yourDog = new Dog("Bowser", 3);
            yourDog = new Dog("Hank", 5);

Now the managed heap looks as follows:
929-002
At this point, let’s assume that the Garbage Collector runs, in an effort to reclaim some memory. Notice that the Bowser object is no longer referenced by any reference-typed variable or by another object. Bowser gets marked for collection:

929-003

All objects not marked for collection are compacted, moving them to the front (left) of the heap.  After the Garbage Collector runs, the heap is as shown below.  Note that the memory for the Bowser object is reclaimed by the heap.

929-004

#928 – How Objects Are Removed from the Managed Heap

The Garbage Collector (GC) manages the portion of virtual memory known as the managed heap, where referenced-typed objects are stored.  When an object is no longer referenced, it is destroyed and its memory made available for other objects.

More specifically:

  • The GC runs when available physical memory is low or when the amount of memory allocated on the heap passes some threshold
  • The GC follows all references to build a list of “reachable” objects
  • For all objects that are no longer reachable
    • If the object has a finalizer, it is marked for finalization (but not collected during this pass of the GC)
    • If the object doesn’t have a finalizer, it is marked for collection
  • Objects not marked for collection are compacted–moved to the start of the heap
  • References to moved objects are updated
  • The pointer to the end of the heap is updated (where new objects are allocated)

#927 – Visualizing How Objects Are Created on the Managed Heap

Suppose that you create a couple of new objects and refer to them using referenced-typed variables, as follows:

            Dog myDog = new Dog("Kirby", 15);
            Dog yourDog = new Dog("Bowser", 3);

You now have two variables of type Dog, each referring to an instance of a Dog object.  The Dog objects exist on the managed heap.

927-001

Suppose that you now change the yourDog variable to also reference the “Kirby” dog, as follows:

            yourDog = myDog;

The managed heap still contains the two original Dog objects. But notice that the “Bowser” dog is no longer currently referenced by any reference-typed variables.  Bowser is no longer reachable from any of the reference-typed variables, but this Dog object has not yet been destructed, or its memory reclaimed.

972-002

#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

 

#925 – The Managed Heap

The managed heap is an area of the virtual memory available to your process that the Common Language Runtime (CLR) uses for storing instances of reference types (objects) that you create.  The Garbage Collector (GC) is the component in the CLR that is reponsible for allocating memory on the heap for your objects and for releasing that memory when the objects are no longer referenced.

 

#924 – You Shouldn’t Normally Worry about Memory Management

Because the Common Language Runtime (CLR) implements automatic memory management for heap-based objects, you generally don’t need to worry about memory management for objects that your code creates.  Because the Garbage Collector (GC) takes care of automatically deleting objects that are no longer being referenced, you don’t need to worry about deleting objects or removing references to them.

Your code explicitly creates objects, but doesn’t need to worry about destroying them, or about releasing memory for objects no longer in use.

Your code can freely:

  • Create objects (instances of types)
  • Create references to objects
  • Change reference-type variables to refer to other objects
  • Allow objects to contain references to other objects

Your code typically will not:

  • Dereference objects (e.g. by setting a reference-typed variable to null)
  • Keep track of whether objects have been deleted or not
  • Interact with the Garbage Collector to impact how objects are destroyed

#921 – Objects Are Explicitly Created but Automatically Destroyed

You create an object, or an instance of a classusing the new keyword.  When you create the object, you must also refer to it using a reference-typed variable.

            // Create an instance of a Dog and reference it using
            // the "myDog" variable.
            Dog myDog = new Dog("Kirby", 15);

Using the new keyword, you explicitly create objects.

A;though you create objects explicitly, you never explicitly delete them.  Instead, an object can be deleted after it is no longer being referenced by any reference-typed variables.  Once the object has been deleted, the CLR (Common Language Runtime) will reclaim the memory that was being used by the object.

Not only do you not explicitly delete objects, you also can’t predict when the object will be deleted.  The CLR will decide when to delete the object, based on when it needs the memory that the object is using.

#668 – GetTotalMemory Indicates How Much Memory You’ve Allocated

You can call the GC.GetTotalMemory function to find out the total # bytes you’ve allocated so far on the managed heap.  Note that this number does not include other memory that your process may have allocated, including things like memory allocated in unmanaged code.

        static void Main()
        {
            try
            {
#if DEBUG
                GC.Collect();
#endif
                Console.WriteLine(string.Format("Before we start, total mem alloc'd {0} bytes", GC.GetTotalMemory(false)));

                while (true)
                {
                    ConsoleKeyInfo cki = Console.ReadKey();

                    int size = 1024 * 1024 * 100;  // 100MB
                    byte[] data = new byte[size];

                    Array.Clear(data, 0, size);

                    _bigList.Add(data);

#if DEBUG
                    GC.Collect();
#endif
                    Console.WriteLine(string.Format("Total mem alloc'd now {0} bytes", GC.GetTotalMemory(false)));
                }
            }
            catch (Exception xx)
            {
                Console.WriteLine(xx.ToString());
                Console.ReadLine();
                }
        }

#667 – .NET Memory Performance Counters Updated When Garbage Collector Runs

The .NET CLR Memory performance counters can be used to track memory usage for CLR-based (.NET) applications.  The counters, however, are only updated when the garbage collector runs.  This means that if you’re watching one of the counters, you won’t necessarily see it update immediately after your application allocates some memory.

If you are doing some debugging or profiling related to memory management, you can force garbage collection at the point where you want to measure memory usage.  You do this using the GC.Collect method.  GC.Collect should not typically be called explicitly in production code.

    public class Program
    {
        private static List<object> _bigList = new List<object>();

        static void Main()
        {
            try
            {
#if DEBUG
                GC.Collect();
#endif

                while (true)
                {
                    ConsoleKeyInfo cki = Console.ReadKey();

                    int size = 1024 * 1024;  // 1MB
                    byte[] data = new byte[size];

                    Array.Clear(data, 0, size);

                    _bigList.Add(data);

#if DEBUG
                    GC.Collect();
#endif
                }
            }
            catch (Exception xx)
            {
                Console.WriteLine(xx.ToString());
                Console.ReadLine();
                }
        }
    }