Consider the below code:
public class Class1
{
public static int c;
~Class1()
{
c++;
}
}
public class Class2
{
public static void Main()
{
{
var c1=new Class1();
//c1=null; // If this line is not commented out, at the Console.WriteLine call, it prints 1.
}
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine(Class1.c); // prints 0
Console.Read();
}
}
Now, even though the variable c1 in the main method is out of scope and not referenced further by any other object when GC.Collect() is called, why is it not finalized there?
You are being tripped up here and drawing very wrong conclusions because you are using a debugger. You'll need to run your code the way it runs on your user's machine. Switch to the Release build first with Build + Configuration manager, change the "Active solution configuration" combo in the upper left corner to "Release". Next, go into Tools + Options, Debugging, General and untick the "Suppress JIT optimization" option.
Now run your program again and tinker with the source code. Note how the extra braces have no effect at all. And note how setting the variable to null makes no difference at all. It will always print "1". It now works the way you hope and expected it would work.
Which does leave with the task of explaining why it works so differently when you run the Debug build. That requires explaining how the garbage collector discovers local variables and how that's affected by having a debugger present.
First off, the jitter performs two important duties when it compiles the IL for a method into machine code. The first one is very visible in the debugger, you can see the machine code with the Debug + Windows + Disassembly window. The second duty is however completely invisible. It also generates a table that describes how the local variables inside the method body are used. That table has an entry for each method argument and local variable with two addresses. The address where the variable will first store an object reference. And the address of the machine code instruction where that variable is no longer used. Also whether that variable is stored on the stack frame or a cpu register.
This table is essential to the garbage collector, it needs to know where to look for object references when it performs a collection. Pretty easy to do when the reference is part of an object on the GC heap. Definitely not easy to do when the object reference is stored in a CPU register. The table says where to look.
The "no longer used" address in the table is very important. It makes the garbage collector very efficient. It can collect an object reference, even if it is used inside a method and that method hasn't finished executing yet. Which is very common, your Main() method for example will only ever stop executing just before your program terminates. Clearly you would not want any object references used inside that Main() method to live for the duration of the program, that would amount to a leak. The jitter can use the table to discover that such a local variable is no longer useful, depending on how far the program has progressed inside that Main() method before it made a call.
An almost magic method that is related to that table is GC.KeepAlive(). It is a very special method, it doesn't generate any code at all. Its only duty is to modify that table. It extends the lifetime of the local variable, preventing the reference it stores from getting garbage collected. The only time you need to use it is to stop the GC from being to over-eager with collecting a reference, that can happen in interop scenarios where a reference is passed to unmanaged code. The garbage collector cannot see such references being used by such code since it wasn't compiled by the jitter so doesn't have the table that says where to look for the reference. Passing a delegate object to an unmanaged function like EnumWindows() is the boilerplate example of when you need to use GC.KeepAlive().
So, as you can tell from your sample snippet after running it in the Release build, local variables can get collected early, before the method finished executing. Even more powerfully, an object can get collected while one of its methods runs if that method no longer refers to this. There is a problem with that, it is very awkward to debug such a method. Since you may well put the variable in the Watch window or inspect it. And it would disappear while you are debugging if a GC occurs. That would be very unpleasant, so the jitter is aware of there being a debugger attached. It then modifies the table and alters the "last used" address. And changes it from its normal value to the address of the last instruction in the method. Which keeps the variable alive as long as the method hasn't returned. Which allows you to keep watching it until the method returns.
This now also explains what you saw earlier and why you asked the question. It prints "0" because the GC.Collect call cannot collect the reference. The table says that the variable is in use past the GC.Collect() call, all the way up to the end of the method. Forced to say so by having the debugger attached and by running the Debug build.
Setting the variable to null does have an effect now because the GC will inspect the variable and will no longer see a reference. But make sure you don't fall in the trap that many C# programmers have fallen into, actually writing that code was pointless. It makes no difference whatsoever whether or not that statement is present when you run the code in the Release build. In fact, the jitter optimizer will remove that statement since it has no effect whatsoever. So be sure to not write code like that, even though it seemed to have an effect.
One final note about this topic, this is what gets programmers in trouble that write small programs to do something with an Office app. The debugger usually gets them on the Wrong Path, they want the Office program to exit on demand. The appropriate way to do that is by calling GC.Collect(). But they'll discover that it doesn't work when they debug their app, leading them into never-never land by calling Marshal.ReleaseComObject(). Manual memory management, it rarely works properly because they'll easily overlook an invisible interface reference. GC.Collect() actually works, just not when you debug the app.
[ Just wanted to add further on the Internals of Finalization process ]
You create an object and when the object is garbage collected, the object's Finalize method should be called. But there is more to finalization than this very simple assumption.
CONCEPTS:
Objects not implementing Finalize methods: their memory is reclaimed immediately, unless of course, they are not reachable by application code any more.
Objects implementing Finalize method: the concepts of Application Roots, Finalization Queue, Freachable Queue need to be understood since they are involved in the reclamation process.
Any object is considered garbage if it is not reachable by application code.
Assume: classes/objects A, B, D, G, H do not implement the Finalize method and C, E, F, I, J do implement the Finalize method.
When an application creates a new object, the new operator allocates memory from the heap. If the object's type contains a Finalize method, then a pointer to the object is placed on the finalization queue. Therefore pointers to objects C, E, F, I, J get added to the finalization queue.
The finalization queue is an internal data structure controlled by the garbage collector. Each entry in the queue points to an object that should have its Finalize method called before the object's memory can be reclaimed.
The figure below shows a heap containing several objects. Some of these objects are reachable from the application roots, and some are not. When objects C, E, F, I, and J are created, the .NET framework detects that these objects have Finalize methods and pointers to these objects are added to the finalization queue.
When a GC occurs (1st Collection), objects B, E, G, H, I, and J are determined to be garbage. A,C,D,F are still reachable by application code depicted as arrows from the yellow box above.
The garbage collector scans the finalization queue looking for pointers to these objects. When a pointer is found, the pointer is removed from the finalization queue and appended to the freachable queue ("F-reachable", i.e. finalizer reachable). The freachable queue is another internal data structure controlled by the garbage collector. Each pointer in the freachable queue identifies an object that is ready to have its Finalize method called.
After the 1st GC, the managed heap looks something similar to figure below. Explanation given below:
The memory occupied by objects B, G, and H has been reclaimed immediately because these objects did not have a finalize method that needed to be called.
However, the memory occupied by objects E, I, and J could not be reclaimed because their Finalize method has not been called yet. Calling the Finalize method is done by freachable queue.
A, C, D, F are still reachable by application code depicted as arrows from yellow box above, so they will not be collected in any case.
There is a special runtime thread dedicated to calling Finalize methods. When the freachable queue is empty (which is usually the case), this thread sleeps. But when entries appear, this thread wakes, removes each entry from the queue, and calls each object's Finalize method. The garbage collector compacts the reclaimable memory and the special runtime thread empties the freachable queue, executing each object's Finalize method. So here finally is when your Finalize method gets executed.
The next time the garbage collector is invoked (2nd GC), it sees that the finalized objects are truly garbage, since the application's roots don't point to it and the freachable queue no longer points to it (it's EMPTY too), therefore the memory for the objects E, I, J may be reclaimed from the heap. See figure below and compare it with figure just above.
The important thing to understand here is that two GCs are required to reclaim memory used by objects that require finalization. In reality, more than two collections cab be even required since these objects may get promoted to an older generation.
NOTE: The freachable queue is considered to be a root just like global and static variables are roots. Therefore, if an object is on the freachable queue, then the object is reachable and is not garbage.
As a last note, remember that debugging application is one thing, garbage collection is another thing and works differently. So far you can't feel garbage collection just by debugging applications. If you wish to further investigate memory get started here.
Related
Before you report me for asking a commonly-asked question, hear me out.
I am writing a program to read from a datalogger using C# and windows forms. Each of the channels can mean different things, such as lift, drag, and pressure. I plan to make these channels configurable. Here is how such a channel is initialized:
private Channel Lift_Channel = new Channel(1);
This initializes Lift_Channel as a Channel object that references channel 1 on the logger.
However, the constructor method Channel(int) is the only method I can use to set the channel. If I want Lift_Channel to point to channel 2, I would like to do something like this.
delete Lift_Channel;
Lift_Channel = new Channel(2);
I feel like if I can't delete Lift_Channel then recreate it, I will have memory leak errors because the Channel(1) data will be floating around.
Any ideas?
Nope, nothing to delete. After you re-assign it, Lift_Channel will no longer point to the old memory address, and the memory will be cleaned up.
In C++, any object allocated on the heap must be deleted or it will leak memory. In a typical .NET managed app that only uses managed code, developers don't have to worry about deleting an object when they are done using it. This is because .NET has a very nice and efficient garbage collector. It keeps track of all references to objects and when the last reference to an object is no longer valid, it then deletes the object on its own. The garbage collector typically runs in about the same time as a memory page fault, so it is pretty efficient.
There are times when you might need to implement some code to handle cleaning up an object if it is using unmanaged code. .NET provides an interface for this called IDisposable. The main idea behind this is for the code that instantiates the object to call the Dispose method to perform cleanup. If someone forgets to do this, then the garbage collector ends up calling your finalizer method as a fail safe. The finalizer needs to implement a call to the Dispose method. An example of this is shown in the link above.
The garbage collector (GC) runs automatically in the background and stops all active managed threads before it does any clean up work. Part of this clean up work involves compaction of memory and it can move memory around. If you have calls into unmanaged code that use pointers or references to managed data, then that data should be pinned in memory so that the GC won't move it. This can be done with the Fixed statement for use within a given method or the GCHandle.Alloc method for longer periods of time that can be used outside of the method it was declared in. The GCHandle Free method should be called to release the pin. Pinning memory requires that the code be marked as unsafe.
.NET provides a class called GC that can be used to help control the garbage collector. For example, if you have objects that are using a large amount of memory and wish to wait for the memory to be released before proceeding, then the following methods should be called to accomplish this after setting them to null:
GC.Collect();
GC.WaitForPendingFinalizers();
.Net garbage collects allocated items that cannot be reached any more. collection. The garbage collector runs periodically and will reclaim memory for objects that are not reachable by anything else.
By assigning via a new new, the memory formerly associated with a variable (before the reassignment) will not longer be reachable (assuming that variable was the only thing referring to it). So the old object becomes unreachable, then the garbage collector will pick it up the next time it runs.
Hence, there is no delete operator.
You can assign null to a variable, to make it unreachable or
you can assign a new object reference to a variable.
Finally, you can use the IDisposable interface to ensure disposal at the end of a block of code.
All of those actions achieve the effect of a delete. (Eventually, if the item is unreachable, it will be "deleted when the garbage collector runs).
I've read some docs about the .NET Garbage Collector but i still have some doubts (examples in C#):
1)Does GC.Collect() call a partial or a full collection?
2)Does a partial collection block the execution of the "victim" application? If yes.. then i suppose this is a very "light" things to do since i'm running a game server that uses 2-3GB of memory and i "never" have execution stops (or i can't see them..).
3)I've read about GC roots but still can't understand how exactly they works. Suppose that this is the code (C#):
MyClass1:
[...]
public List<MyClass2> classList = new List<MyClass2>();
[...]
Main:
main()
{
MyClass1 a = new MyClass1();
MyClass2 b = new MyClass2();
a.classList.Add(b);
b = null;
DoSomeLongWork();
}
Will b ever be eligible to be garbage collected(before the DoSomeLongWork finishes)? The reference to b that classList contains, can it be considered a root?
Or a root is only the first reference to the instance? (i mean, b is the root reference because the instantiation happens there).
There are a couple of overloads for GC.Collect. The one without any parameters does a full collect. But keep in mind that it is rarely a good idea to call GC explicitly.
Garbage collections occurs within a managed application. The GC may have to suspend all managed threads in that process in order to compact the heap. It doesn't affect other processes on the system if that is what you're asking. For most applications it is usually not an issue, but you may experience performance related problems due to frequent collects. There are a couple of relevant performance counters (% time in GC, etc.) that you can use to monitor how the GC is performing.
Think of a root as a valid reference. In your example the instance of MyClass2 will not be collected if there are still references to it. I.e. if the instance pointed to by a is still rooted so will your instance of MyClass2. Also, keep in mind that GC is much more aggressive in release mode builds than debug builds.
GC.Collect() does a full collection. That's why it's not a good idea to call it yourself, as it can prematurely promote objects up a generation
AFAIK, not really. The GC blocks for so little time as to be inconsequential.
A stack GC root is an object that is currently being referenced by a method on the stack. Note that the behaviour is different between debug and release builds; on debug builds all variables in a method are kept alive until the method returns (so you can see their value in the debug window). On release builds the CLR keeps track of where method variables are used and the GC can remove objects that are referenced by the currently executing method but aren't used in the section of the method that is still to execute.
So, in your example, both a and b are not referenced again after the DoSomeLongWork() call, so in release builds, during that method execution they would both be eligible for collection. In debug builds they would hang around until the main() method returns.
Garbage collection is automatic. You don't have to interfere unless you are dealing with unmanaged resources. Garbage gets collected whenever the object is going out of scope; and at specific intervals whenever garbage-collector deems necessary - for instance, OS requires memory. This means there is no guarantee on how soon that will be, but your application will not run out of memory before memory from those objects is reclaimed.
Will b ever be eligible to be garbage collected(before the DoSomeLongWork finishes)?
Yes, whenever garbage collector finds it necessary.
Checkout Garbage Collector Basics and Performance Hints
Yes, GC.Collect() does a full collect, but you can do a manual GC.Collect(0) to do the gen 0 only.
All collections block the threads, but a partial collect does so only very briefly.
No, the instance of MyClass2 is still reachable in the other list. Both a and b are root references (during DoSomeLongWork) but since b is null that doesn't matter. Note that the GC is very tied in with the concept of reference types. b is only a local var referencing the anonymous object. The 'roots' for the GC are static fields, everything on the stack and even CPU registers. In a simpler way: everything your code still has access to.
Will b ever be eligible to be garbage collected (before the DoSomeLongWork finishes)?
Potentially yes, provided a and b are expelled from the set of global roots by the compiler (i.e. not kept on the stack) then they can be reclaimed by a collection during DoSomeLongWork.
I have found cases where .NET reclaims happily but Mono leaks memory.
The reference to b that classList contains, can it be considered a root?
Whether or not b will be turned into a global root depends entirely upon the compiler.
A toy compiler might push references from function arguments and returned from function calls onto the stack and unwind the stack at the end of each function. In this case, b will be pushed onto the stack and, therefore, will be a global root.
Production quality compilers perform sophisticated register allocation and maintain only live references on the stack, either overwriting or nulling references on the stack as they die. In this case, b is dead during the call to DoSomeLongWork so its stack entry will have been nulled or overwritten.
None of this can be inferred from the source code without details of what exactly the compiler will do. For example, my HLVM project is only a toy at this stage, using the former technique, but it will actually collect a and b in this case because the call to DoSomeLongWork is a tail call.
Or a root is only the first reference to the instance?
The global roots are the references the GC begins with in order to traverse all of the reachable data in the heap. The global roots are typically global variables and threads' stacks but more sophisticated GC algorithms can introduce new kinds of global roots, e.g. remembered sets.
Should be an easy one. Let's say I have the following code:
void Method()
{
AnotherMethod(new MyClass());
}
void AnotherMethod(MyClass obj)
{
Console.WriteLine(obj.ToString());
}
If I call "Method()", what happens to the MyClass object that was created in the process? Does it still exist in the stack after the call, even though nothing is using it? Or does it get removed immediately?
Do I have to set it to null to get GC to notice it quicker?
After the call to Method completes, your MyClass object is alive but there are no references to it from a rooted value. So it will live until the next time the GC runs where it will be collected and the memory reclaimed.
There is really nothing you can do to speed up this process other than to force a GC. However this is likely a bad idea. The GC is designed to clean up such objects and any attempt you make to make it faster will likely result in it being slower overall. You'll also find that a GC, while correctly cleaning up managed objects, may not actually reduce the memory in your system. This is because the GC keeps it around for future use. It's a very complex system that's typically best left to it's own devices.
If I call "Method()", what happens to the MyClass object that was created in the process?
It gets created on the GC heap. Then a reference to its location in the heap is placed on the stack. Then the call to AnotherMethod happens. Then the object's ToString method is called and the result is printed out. Then AnotherMethod returns.
Does it still exist in the stack after the call, even though nothing is using it?
Your question is ambiguous. By "the call" do you mean the call to Method, or AnotherMethod? It makes a difference because at this point, whether the heap memory is a candidate for garbage collection depends upon whether you compiled with optimizations turned on or off. I'm going to slightly change your program to illustrate the difference. Suppose you had:
void Method()
{
AnotherMethod(new MyClass());
Console.WriteLine("Hello world");
}
With optimizations off, we sometimes actually generate code that would be like this:
void Method()
{
var temp = new MyClass();
AnotherMethod(temp);
Console.WriteLine("Hello world");
}
In the unoptimized version, the runtime will actually choose to treat the object as not-collectable until Method returns, after the WriteLine. In the optimized version, the runtime can choose to treat the object as collectible as soon as AnotherMethod returns, before the WriteLine.
The reason for the difference is because making object lifetime more predictable during debugging sessions often helps people understand their programs.
Or does it get removed immediately?
Nothing gets collected immediately; the garbage collector runs when it feels like it ought to run. If you need some resource like a file handle to be cleaned up immediately when you're done with it then use a "using" block. If not, then let the garbage collector decide when to collect memory.
Do I have to set it to null to get GC to notice it quicker?
Do you have to set what to null? What variable did you have in mind?
Regardless, you do not have to do anything to make the garbage collector work. It runs on its own just fine without prompting from you.
I think you're overthinking this problem. Let the garbage collector do its thing and don't stress about it. If you're having a real-world problem with memory not being collected in a timely manner, then show us some code that illustrates that problem; otherwise, just relax and learn to love automatic storage reclamation.
Actually, the instance will be declared on the heap, but have a look at Eric Lipper's article, The Stack is an Implementation Detail.
In any case, because there will be no more references to the instance after the function executes, it will be (or, more accurately, can be) deleted by the garbage collector at some undefined point in the future. As to exactly when that happens, it's undefined, but you also (essentially) don't need to worry about it; the GC has complicated algorithms that help it determine what and when to collect.
Does it still exist in the stack after the call
Semantics are important here. You asked if it still exists on the stack after the method call. The answer there is "no". It was removed from the stack. But that's not the final story. The object does still exist, it's just no longer rooted. It won't be destroyed or collected until the GC runs. But at this point it's no longer your concern. The GC is much better at deciding when to collect something than you or I are.
Do I have to set it to null to get GC to notice it quicker?
There's almost never a good reason to do that, anyway. The only time that helps is if you have a very long running method and an object that you are done with early that otherwise won't go out of scope until the end of the method. Even then, setting it to null will only help in the rare case where the GC decides to run during the method. But in that case you're probably doing something else wrong as well.
In C# the new MyClass() is scoped only to live inside Method() while it is active. Once that AnotherMethod() is finished executing, it is out of scope and gets unrooted. It then remains on the heap until the GC runs its collection cycle and identifies it as an unreferenced memory block. So it is still "alive" on the heap but it is inaccessible.
The GC keeps track of what objects can possibly still be referenced later in the code. It then, at intervals, checks to see if there are any objects still alive that could not possibly be referenced later in the code, and cleans them up.
The mechanics of this are somewhat complex, and when these collections will happen depends on a variety of factors. The GC is designed to do these collections at the most optimal time (that it can establish) and so, while it is possible to force it to do a collection, it is almost always a bad idea.
Setting the variable to null will have very little overall effect on how soon the object is dealt with. While it can, in some small corner cases, be of benefit it is not worth littering your code with redundant assignments which will not affect your codes performance and only harm readability.
The GC is designed to be as effective as possible without you needing to think about it. To be honest, the only thing you really need to be mindful of is being careful when allocating really large objects that will stay alive for a long time, and that's generally quite rare in my experience.
As far as I know, the object is only valid inside your method context. After the method "Method()" is executed it is added to the dispose queue.
This is a .net CLR related question.
I have 3 objects object A,B,C
A refres B and B refers c
What happens to these objects in the heap if i kill the Object "A" explicitly.Which will be garbage collected?(Object A or B or c or all?)
Can some one explain the garbage collection process in this scenario in detail.
Thanks in advance
SNA
First - you can't "kill the Object "A" explicitly"; you can clear the reference to it, but that just sets a local variable to null, and has nothing to do with the actual object on the managed heap. You can Dispose() it, but that is nothing to do with GC.
It all depends; can anything else see B / C? If not, they are eligible for collection. But GC is non-deterministic; it only happens when it chooses. At some indeterminate time, GC will kick in, detect that these objects are unreachable, and remove them. In the process, it will check for any that have finalizers (that are still outstanding), and execute the finalizers (in a 2-step process).
One thing people often forget about in terms of reachability is events; if B/C subscribe to events on a long-lived object, then B/C are reachable (by the event publisher).
To clarify; GC works by building a tree from the root objects (threads, etc). It walks every reference, flagging all the objects that can be reached. Anything that isn't flagged at the end is eligible for collection. This avoids the COM/COM+ issue of getting memory leaks due to disconnected islands of data, where X => Y and Y => X (so both X and Y have positive ref-counts, so neither gets cleared).
The first misunderstanding might be that you cannot kill a managed object explicitly.
You can free unmanaged memory that you allocated yourself, but that isn't managed and by that not subject to garbage collection anyway.
When you set the reference to A to null or it runs out of scope, then there wouldn't be any references to B & C, and the next GC collection will take care of it.
In .NET there is no way to actually kill/remove an object. All you can do explicitly is to disose an object. This nothing more that a simple call to Dispose() on your object. This will allow you to cleanup your object before it might get collected by the garbage collector at a later time (that you can not really infulence). See IDisposable on more details. The 2nd option to get a chance to cleanup your object before it gets collected by the GC is to implement a finalizer. Unlike the Dispose() this will be called by the GC automatically. Again, both are just way to cleanup any resources before an object might stop to exist.
So to answer your question if your object A gets "killed" wich only happens when it is not referenced by any other object anymore B and C will get "killed" to IF they are only referenced through A. Usually you do not have any influence on when this actually happens. All you can do is to implement the finalizer to get notified when it happens.
The GC is a background service that runs on a separate thread that follows a complex logic when to actually delete objects.
If you want to get a basic understanding on how the GC works I would suggest the following two articles. Allthough a bit old they still fully apply.
In the following c# code, how do I get rid of the objects when it's no longer useful? Does it get taken care of automatically, or do I need to do something?
public void Test()
{
object MyObject = new object();
... code ...
}
Automatically. When MyObject goes out of scope (at the end of Test), it's flagged for garbage collection. At some point in the future, the .NET runtime will clean up the memory for you
Edit:
(I should point out for the sake of completeness that if you pass MyObject somewhere, it gets passed by reference, and will not be garbage collected - it's only when no more references to the object are floating around that the GC is free to collect it)
Edit: in a release build, MyObject will usually be collected as soon as it's unused (see my answer for more details --dp)
The short answer is: unless it has unmanaged resources (file handles etc) you don't need to worry.
The long answer is a bit more involved.
When .NET decides it wants to free up some memory, it runs the garbage collector. This looks for all the objects which are still in use, and marks them as such. Any local variable (in any stack frame of any thread) which may still be read counts as a root as do static variables. (In fact I believe that static variables are referenced via live Type objects, which are referenced via live AppDomain objects, but for the most part you can regard static variables as roots.)
The garbage collector looks at each object referred to by a root, and then finds more "live" references based on the instance variables within those objects. It recurses down, finding and marking more and more objects as "live". Once it's finished this process, it can then look at all the rest of the objects and free them.
That's a very broad conceptual picture - but it gets a lot more detailed when you think of the generational model of garbage collection, finalizers, concurrent collection etc. I strongly recommend that you read Jeff Richter's CLR via C# which goes into this in a lot of detail. He also has a two part article (back from 2000, but still very relevant) if you don't want to buy the book.
Of course all this doesn't mean you don't need to worry about memory usage and object lifetimes in .NET. In particular:
Creating objects pointlessly will cost performance. In particular, the garbage collector is fast but not free. Look for simple ways to reduce your memory usage as you code, but micro-optimising before you know you have a problem is also bad.
It's possible to "leak" memory by making objects reachable for longer than you intended. Two reasonably common causes of this are static variables and event subscriptions. (An event subscription makes the event handler reachable from the event publisher, but not the other way round.)
If you use more memory (in a live, reachable way) than you have available, your app will crash. There's not a lot .NET can do to prevent that!
Objects which use non-memory resources typically implement IDisposable. You should call Dispose on them to release those resources when you're finished with the object. Note that this doesn't free the object itself - only the garbage collector can do that. The using statement in C# is the most convenient way of calling Dispose reliably, even in the face of an exception.
The other answers are correct, unless your object is an instance of a class which implements the IDisposable interface, in which case you ought to (explicitly, or implicitly via a using statement) call the object's Dispose method.
In optimized code, it is possible and likely that MyObject will be collected before the end of the method. By default, the debug configuration in Visual Studio will build with the debug switch on and the optimize switch off which means that MyObject will be kept to the end of the method (so that you can look at the value while debugging). Building with optimize off (debug doesn't matter in this case) allows MyObject to be collected after it's determined to be unused. One way to force it to stay alive to the end of the method is to call GC.KeepAlive(MyObject) at the end of the method.
This will force the garbage collector to get rid of unused objects.
GC.Collect();
GC.WaitForPendingFinalizers();
If you want a specific object to be collected.
object A = new Object();
...
A = null;
GC.collect();
GC.WaitForPendingFinalizers();
It gets taken care of automatically.
Typically garbage collection can be depended on to cleanup, but if your object contains any unmanaged resources (database connections, open files etc) you will need to explicitly close those resources and/or exceute the dispose method on the object (if it implements IDisposable). This can lead to bugs so you need to be careful how you deal with these types of objects. Simply closing a file after using it is not always sufficient since an exception before the close executes will leave the file open. Either use a using block or a try-finally block.
Bottom line: "using" is your friend.