The document discusses classes, objects, and references in C#. It explains that the new keyword returns a reference to an object on the heap rather than the actual object. It also covers object lifetime, how the garbage collector determines when an object is no longer needed, and how objects are assigned to generations by the garbage collector to optimize memory management.
1. USM’s Shriram Mantri Vidyanidhi Info Tech Academy
Classes, Objects, and References
After a class has been defined, you may allocate any number of
objects using the C# new keyword.
Understand, however, that the new keyword returns a
reference to the object on the heap, not the actual object itself.
2. USM’s Shriram Mantri Vidyanidhi Info Tech Academy
If you declare the reference variable as a local variable in a method
scope, it is stored on the stack for further use in your application.
Eg. You create a object in Main method
When you want to invoke members on the object, apply the C#
dot operator to the stored reference
3. USM’s Shriram Mantri Vidyanidhi Info Tech Academy
The Basics of Object Lifetime
When you are building your C# applications, you are correct to
assume that the .NET runtime environment (a.k.a. the CLR) will take
care of the managed heap without your direct intervention.
In fact, the golden rule of .NET memory management is simple:
Rule Allocate a class instance onto the managed heap using the new
keyword and forget about it.
4. USM’s Shriram Mantri Vidyanidhi Info Tech Academy
Once instantiated, the garbage collector will destroy an object when it is
no longer needed.
The next obvious question, of course, is, “How does the garbage collector
determine when an object is no longer needed?”
The short (i.e., incomplete) answer is that the garbage collector removes an
object from the heap only if it is unreachable by any part of your code base.
The managed heap maintains a pointer (commonly referred to as the next
object pointer or new object pointer) that identifies exactly where the next
object will be located.
5. USM’s Shriram Mantri Vidyanidhi Info Tech Academy
That said, the newobj instruction tells the CLR to perform the following core
operations:
• Calculate the total amount of memory required for the object to be allocated
(including the memory required by the data members and the base classes).
• Examine the managed heap to ensure that there is indeed enough room to
host the object to be allocated.
If there is, the specified constructor is called and the caller is ultimately
returned a reference to the new object in memory, whose address just
happens to be identical to the last position of the next object pointer.
• Finally, before returning the reference to the caller, advance the next
object pointer to point to the next available slot on the managed heap.
7. USM’s Shriram Mantri Vidyanidhi Info Tech Academy
As your application is busy allocating objects, the space on the
managed heap may eventually become full.
When processing the newobj instruction, if the CLR determines that
the managed heap does not have sufficient memory to allocate the
requested type, it will perform a garbage collection in an attempt to
free up memory.
Thus, the next rule of garbage collection is also quite simple:
Rule If the managed heap does not have sufficient memory to allocate
a requested object, a garbage collection will occur.
8. USM’s Shriram Mantri Vidyanidhi Info Tech Academy
During a garbage collection process, the runtime will investigate objects
on the managed heap to determine whether they are still reachable (i.e.,
rooted) by the application. To do so, the CLR will build
an object graph, which represents each reachable object on the heap.
To do so, the CLR will build an object graph, which represents each
reachable object on the heap.
Assume the managed heap contains a set of objects named A, B, C, D, E, F,
and G. During a garbage collection, these objects (as well as any internal
object references they may contain) are examined for active roots.
After the graph has been constructed, unreachable objects (which we will
assume are objects C and F) are marked as garbage.
9. USM’s Shriram Mantri Vidyanidhi Info Tech Academy
Figure 13-3 diagrams a possible object graph for the scenario just described
(you can read the directional arrows using the phrase depends on or requires,
for example,
E depends on G and B, A depends on nothing, and so on).
10. USM’s Shriram Mantri Vidyanidhi Info Tech Academy
To help optimize the process, each object on the heap is assigned to a
specific “generation.”
The idea behind generations is simple:
the longer an object has existed on the heap, the more likely it is to
stay there.
For example, the class that defined the main window of a desktop
application will be in memory until the program terminates.
Conversely, objects that have only recently been placed on the heap
(such as an object allocated within a method scope) are likely to be
unreachable rather quickly.
11. USM’s Shriram Mantri Vidyanidhi Info Tech Academy
Given these assumptions, each object on the heap belongs to one of the
following generations:
• Generation 0: Identifies a newly allocated object that has never been
marked for collection.
• Generation 1: Identifies an object that has survived a garbage
collection (i.e., it was marked for collection but was not removed due
to the fact that the sufficient heap space was acquired).
• Generation 2: Identifies an object that has survived more than one sweep
of the garbage collector.
12. USM’s Shriram Mantri Vidyanidhi Info Tech Academy
The garbage collector will investigate all generation 0 objects first. If
marking and sweeping (or said more plainly, getting rid of) these objects
results in the required amount of free memory, any surviving objects are
promoted to generation 1.
13. USM’s Shriram Mantri Vidyanidhi Info Tech Academy
To see how an object’s generation affects the collection process, ponder
Figure 13-5, which diagrams how a set of surviving generation 0 objects
(A, B, and E) are promoted once the required memory has been reclaimed.
14. USM’s Shriram Mantri Vidyanidhi Info Tech Academy
If all generation 0 objects have been evaluated, but additional memory is
still required, generation 1 objects are then investigated for reachability
and collected accordingly.
Surviving generation 1 objects are then promoted to generation 2. If the
garbage collector still requires additional memory, generation 2 objects
are evaluated.
If the garbage collector still requires additional memory, generation 2
objects are evaluated.
15. USM’s Shriram Mantri Vidyanidhi Info Tech Academy
At this point, if a generation 2 object survives a garbage collection, it
remains a generation 2 object, given the predefined upper limit of object
generations.
The bottom line is that by assigning a generational value to objects on
the heap, newer objects (such as local variables) will be removed
quickly, while older objects (such as a program’s main Window) are not
“bothered” as often.
16. USM’s Shriram Mantri Vidyanidhi Info Tech Academy
The mscorlib.dll assembly provides a class type named System.GC that
allows you to programmatically interact with the garbage collector using
a set of static members.
The System.GC Type
Now, do be very aware that you will seldom (if ever) need to make use of
this class directly in your code.
Typically, the only time you will use the members of System.GC is when
you are creating classes that make internal use of unmanaged resources.
17. USM’s Shriram Mantri Vidyanidhi Info Tech Academy
Again, the whole purpose of the .NET garbage collector is to manage
memory on our behalf.
Forcing a Garbage Collection
However, in some very rare circumstances, it may be beneficial to
programmatically force a garbage collection using GC.Collect().
Finalize() will (eventually) occur during a “natural”
garbage collection or
possibly when you programmatically force a collection via
GC.Collect().
18. USM’s Shriram Mantri Vidyanidhi Info Tech Academy
Lets see generation of object
using System;
namespace SimpleGC
{
class Program
{
static void Main(string[] args)
{
// Print out estimated number of bytes on heap.
Console.WriteLine("Estimated bytes on heap: {0}",
GC.GetTotalMemory(false));
// MaxGeneration is zero based.
Console.WriteLine("This OS has {0} object generations.n",
(GC.MaxGeneration + 1));
Car refToMyCar = new Car("Zippy", 100);
Console.WriteLine(refToMyCar.ToString());
// Print out generation of refToMyCar.
Console.WriteLine("nGeneration of refToMyCar is: {0}",
GC.GetGeneration(refToMyCar));//0
}
}
}
GC.GetTotalMemory(false)=----
Retrieves the number of bytes currently
thought to be allocated. A parameter
// indicates whether this method
can wait a short interval before returning,
// to allow the system to collect
garbage and finalize objects.
GC.MaxGeneration
Gets the maximum number of generations
that the system currently supports
Returns:
// A value that ranges from
zero to the maximum number of supported
generations.
19. USM’s Shriram Mantri Vidyanidhi Info Tech Academy
public class Car
{
public int CurrentSpeed { get; set; }
public string PetName { get; set; }
public Car() { }
public Car( string name, int speed )
{
PetName = name;
CurrentSpeed = speed;
}
public override string ToString()
{
return string.Format("{0} is going {1}
MPH",
PetName, CurrentSpeed);
}
}
How to force GC:
GC.Collect(0, GCCollectionMode.Forced);
GC.WaitForPendingFinalizers();
When you manually force a garbage collection,
you should always make a call to GC.WaitForPendingFinalizers().
With this approach, you can rest assured that all finalizable objects have
had a chance to perform any necessary clean up before your
program continues.
Under the hood, GC.WaitForPendingFinalizers() will suspend the calling
thread during the collection process.
This is a good thing, as it ensures your code does not invoke methods
on an object currently being destroyed!
20. USM’s Shriram Mantri Vidyanidhi Info Tech Academy
sing System;
namespace SimpleGC
{
class Program
{
static void Main(string[] args)
{
Car refToMyCar = new Car("Zippy", 100);
Console.WriteLine(refToMyCar.ToString());
// Print out generation of refToMyCar.
Console.WriteLine("nGeneration of refToMyCar is: {0}",
GC.GetGeneration(refToMyCar));
// Make a ton of objects for testing
purposes.
object[] tonsOfObjects = new
object[90000];
for (int i = 0; i <90000; i++)
{
tonsOfObjects[i] = new object();
MakeACar();
}
// Collect only gen 0 objects.
GC.Collect(0, GCCollectionMode.Forced);
GC.WaitForPendingFinalizers();
static void MakeACar()
{
// If myCar is the only reference to the Car object,
// it *may* be destroyed when this method returns.
Car myCar = new Car();
Console.WriteLine("Generation of tonsOfObjects is: {0}",
GC.GetGeneration(myCar));
myCar = null;
}
// Print out generation of refToMyCar.
Console.WriteLine("Generation of refToMyCar is: {0}",
GC.GetGeneration(refToMyCar));
// See if tonsOfObjects[9000] is still alive.
if (tonsOfObjects[0] != null)
{
Console.WriteLine("Generation of tonsOfObjects[0] is: {0}",
GC.GetGeneration(tonsOfObjects[0]));
}
else
Console.WriteLine("tonsOfObjects[0] is no longer alive.");
// Print out how many times a generation has been swept.
Console.WriteLine("nGen 0 has been swept {0} times",
GC.CollectionCount(0));
Console.WriteLine("Gen 1 has been swept {0} times",
GC.CollectionCount(1));
Console.WriteLine("Gen 2 has been swept {0} times",
GC.CollectionCount(2));
Console.ReadLine();
}
21. USM’s Shriram Mantri Vidyanidhi Info Tech Academy
~Destruct()
// Demonstrate a destructor.
using System;
class Destruct
{
public int x;
public Destruct(int i)
{
x = i;
}
// Called when object is recycled.
~Destruct()
{
Console.WriteLine("Destructing " + x);
}
// Generates an object that is immediately destroyed.
public void Generator(int i)
{
Destruct o = new Destruct(i);
}
}
class DestructDemo
{
static void Main()
{
int count;
Destruct ob = new Destruct(0);
/* Now, generate a large number of objects. At
some point, garbage collection will occur.
Note: you might need to increase the number
of objects generated in order to force
garbage collection. */
for (count = 1; count < 100000; count++)
ob.Generator(count);
Console.WriteLine("Done");
}
}
Destructors
It is possible to define a method that will be called just
prior to an object’s final destruction by the garbage
collector
Destructor implicitly calls the Finalize method, they
are technically the same.
Here, class-name is the name of the class. Thus, a
destructor is declared like a constructor
except that it is preceded with a ~ (tilde).
Notice it has no return type and takes no
arguments.
22. USM’s Shriram Mantri Vidyanidhi Info Tech Academy
protected override void Finalize()
{
try
{
// Cleanup statements...
}
finally
{
base.Finalize();
}
}
Introducing a 'Finalize' method can interfere with
destructor invocation. Did you intend to declare a
destructor?
1>D:dot_netDay_wiseday114GCSimpleGCProgram.cs(25,29
,25,37): error CS0249: Do not override object.Finalize.
Instead, provide a destructor.
// Called when object is recycled.
~Destruct()
{
Console.WriteLine("Destructing " + x);
}
This code will be replace with Finalize method internally and
you are not supposed to write explicitly
23. USM’s Shriram Mantri Vidyanidhi Info Tech Academy
In C# terms, a destructor and finalizer are basically interchangeable
concepts, and should be used to release unmanaged resources when
a type is collected, for example external handles.
It is very rare that you need to write a finalizer.
The problem with that is that GC is non-deterministic, so
the Dispose() method (via IDisposable) makes it possible to
support deterministic cleanup.
This is unrelated to garbage collection, and allows the caller to release
any resources sooner. It is also suitable for use
with managed resources (in addition to unmanaged), for example if you
have a type that encapsulates (say) a database connection, you might
want disposing of the type to release the connection too.
24. USM’s Shriram Mantri Vidyanidhi Info Tech Academy
Destructor/Finalized Dispose
Its called automatically by GC You can call dispose method
It is just called prior to GC Programmer has control and ensure that resources are
released.
It has same name as class name and no parameter
allowed
It is declared in Interface
// Summary:
// Defines a method to release allocated
resources.
public interface IDisposable
{
// Summary:
// Performs application-defined tasks
associated with freeing, releasing, or
// resetting unmanaged resources.
void Dispose();
}
Eg.
public abstract class DbConnection :
Component, IDbConnection, Idisposable
public abstract class Stream :
MarshalByRefObject, IDisposable