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.
Related
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.
I'm using C# but probably the same in VB.NET. I C++ I would just set a break-point on an objects destructor to know when/if it was deleted/free'd. I understand that in winforms the base class calls SupressFinalize so that form destructors are never called, so I guess I can't do it that way. Is there another method to know if an object has been garbage collected? It seems like a catch-22 because if there was you would probably need a reference to check, but by holding that reference the garbage collected will not crush it.
I've read this What strategies and tools are useful for finding memory leaks in .NET?, and I understand there are tools and/or frameworks out there to handle this "big picture" and I'm sure in a few weeks I'll be trying out several of these methods. For now I just have a really strong feeling I might have a leak related to forms not being deleted, so just want to check this one thing (and I want to know just for the sake of knowing).
I know I can watch Dispose, but I'm pretty sure Dispose can be called but still end up with the form object still being around. To test that theory I created a known-issue where I registered for a callback event in my form, then closed the form without unregistering it. Sure enough, Dispose was called (and "disposing" was true), but later when the event was fired, it still hit my break-point inside the form that was supposedly disposed already.
There are really two issues here:
As for your original question, you can use a WeakReference to monitor an object's existence without affecting its lifetime.
Your underlying question suggests that you have a misunderstanding of what garbage collection is, and how it works. The point of garbage collection is that you should never care if an object has been collected or not. Instead, you should focus on the references to the object, and if they have been reassigned or made inaccessible from rooted references. Don't worry about the instance, worry about the references to it.
The entire concept of a managed language is that you don't need to care when an object is actually garbage collected. Lots and lots of time and effort go into the GC to make sure that it doesn't collect objects that it shouldn't, that it doesn't leave objects that it should collect (when it decides to do a pass over the generation that it's in) and that all of this is done reasonably efficiently. This means that if an object consumes a large amount of managed resources and also implements IDisposable (say, a DataTable or DataSet) is disposed it is still consuming a lat of memory, and disposing of it doesn't make it get garbage collected any quicker (although you should still dispose of it to ensure any managed resources go away).
The GC is built to work best when you leave it alone and let it do it's job rather than interfering with it by trying to, for example, manually cause collections to take place. This is occasionally useful for debugging purposes or learning about your program/the language, but it virtually never belongs in a production application.
Disposing has nothing to do with garbage collection or collecting an object. Disposing is the mechanism in place for dealing with a managed object that is holding onto an unmangaed resource (or another object that is holding onto an unmangaed resource). Disposing of the object is telling it to clear up that unmanaged resource, but it has nothing to do with the garbage collector freeing the managed resources of that object. The destructor is there so that you don't free the managed resources before freeing the unmanaged resources, but it's perfectly acceptable (and in fact should always happen) that the unmanaged resources are cleaned up (via dispose) before the managed resource is freed.
Now, it is still possible, given all of this, for a program to have a memory leak, but there are a few questions you really need to be asking yourself first. How large is the leak? Is it a one off, continuous over time, every time we do function X, etc? What would it take for the program to consume so much memory as to be disruptive (either run out of memory, cause other programs to run out of memory, etc) and is that likely to occur with common or legitimate uses of the program? Usually you don't need to start asking these types of questions until you start getting out of memory exceptions, or notice that you start running out of physical memory for a program that oughtn't to. If you notice these problems then you can start looking around for objects that implement IDisposable that aren't being disposed, to see if you're holding onto references to very large objects (or collections of objects) that you no longer need, etc.
Sorry for the wall of text.
Scenario
Lets say that I've decided I really need to call GC.Collect();
What do I need to do to ensure that an object is prepared to be garbage collected appropriately before I actually call this method?
Would assigning null to all properties of the object be enough? Or just assigning null to the object itself?
If you really need to know why.....
I have a distributed application in WCF that sends a DataContract over the wire every few seconds, containing 8 Dictionaries as DataMembers.
This is a lot of data and when it comes into the Client-side interface, a whole new DataContract object is created and the memory usage of the application is growing so big that I'm getting OutOfMemory Exceptions.
Thanks
EDIT
Thanks for all the comments and answers, it seems that everyone shares the same opinion.
What I cannot understand is how I can possibly dispose correctly because the connection is open constantly.
Once I've copied the data over from the incoming object, I don't need the object anymore, so would simply implementing IDisposable on that DataContract object be enough?
My original problem is here - Distributed OutOfMemory Exceptions
As long as nothing else can see the object it is already eligible for collection; nothing more is required. The key point here is to ensure that nothing else is watching it (or at least, nothing with a longer lifetime):
is it in a field somewhere?
is it in a variable in a method that is incomplete? (an infinite loop or iterator block, perhaps)
is it in a collection somewhere?
has it subscribed to some event?
it is captured in a closure (lambda / anon-method) that is still alive?
I genuinely doubt that GC.Collect() is the answer here; if it was eligible it would have already been collected. If it isn't elgible, calling GC.Collect() certainly won't help and quite possibly will make things worse (by tying up CPU when nothing useful can be collected).
You don't generally need to do anything.
If the object is no longer referenced then it's a candidate for collection. (And, conversely, if the object is still referenced then it's not a candidate for collection, however you "prepare" it.)
You need to clean up any unmanaged resources like database connections etc.
Typically by implementing IDisposable and call Dispose.
If you have a finalizer you should call GC.SuppressFinilize.
The rest is cleaned up by the garbage collector.
Edit:
And, oh, naturally you need to release all references to your object.
But, and here is this big but. Unless you have a very very special case you don't need to call GC.Collect. You probably forgets to release some resources or references, and GC.Collect won't help you with that. Make sure you call Dispose on everything Disposable (preferably with the using-pattern).
You should probably pick up a memory profiler like Ants memory profiler and look where all your memory has gone.
If you have no more direct reference to an object, and you're running out of memory, GC should do this automatically. Do make sure you call .Dispose() on your datacontext.
Calling GC.Collect will hardly ever prevent you from getting OutOfMemory exceptions, because .NET will call GC.Collect itself when it is unable to create a new object due to OOM. There is only one scenario where I can think of and that is when you have unreferenced objects that are registered in the finalizable queue. When these objects reference many other objects it can cause a OOM. The solution to this problem is actually not to call GC.Collect but to ensure that those objects are disposed correctly (and implement the dispose pattern correctly when you created those objects).
Using GC.Collect in general
Since you are trying to get rid of a very large collection, it's totally valid to use GC.Collect(). From the Microsoft docs:
... since your application knows more about its behavior than the runtime does, you could help matters by explicitly forcing some collections. For example, it might make sense for your application to force a full collection of all generations after the user saves his data file.
"Preparing" your objects
From the excellent Performance Considerations for Run-Time Technologies in the .NET Framework (from MSDN):
If you keep a pointer to a resource around, the GC has no way of knowing if you intend to use it in the future. What this means is that all of the rules you've used in native code for explicitly freeing objects still apply, but most of the time the GC will handle everything for you.
So, to ensure it's ready for GC, Make sure that you have no references to the objects you wish to collect (e.g. in collections, events, etc...). Setting the variable to null will mean it's ready for collection before the variable goes out of scope.
Also any object which implements IDisposable should have it's Dispose() method called to clean up unmanaged resources.
Before you use GC.Collect
Since it looks like your application is a server, using the Server GC may resolve your issue. It will likely run more often and be more performant in a multi-processor scenario.
The server GC is designed for maximum throughput, and scales with very high performance.
See the Choosing Which Garbage Collector to Use within Performance Considerations for Run-Time Technologies in the .NET Framework (from MSDN):
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.
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.