When I release com objects by Marshall.ReleaseComObject method, memory usage of application doesnt change. Instead of using Marshall.ReleaseComObject, using Garbage Collector (GC.Collect()) can release memory area of com objects but the UI is getting slow down.
So my question is, what is the best method to release com objects?
Athough a bit outdated (as from ArcGIS 10.0) the rules for releasing and how to do so are pretty well described on http://help.arcgis.com/en/sdk/10.0/arcobjects_net/conceptualhelp/index.html#/Releasing_COM_references/0001000004tm000000/.
There are two ways to do this. Either by using Marshal.ReleaseCOMObject or by the ComReleaser-class which essentially is a wrapper around the former. However you may have multiple references to the exact same com-object, which is why calling ReleaseComObject will not finally release the object, but simply decrease the internal reference-counter by one. Only when that counter is equal to zero the object will actually be released. See here for example:
var f1 = featureClass.GetFeature(1);
// retrieve the exact same feature again
var f2 = featureClass.GetFeature(1);
Although from a .NET-perspective f1 and f2 are comlpletely different objects, the underlying com-object is the same (assuming unique-instancing, which is out of the scope of this question). When calling Marshal.ReleaseComObject on either f1 or f2, you will only decrease the internal reference-counter for this com-object by one, leaving one reference alive.
GC.Collect however has no effect as it can´t handle unmanaged resources which com-objects are. The garbage-collector is only able to release managed resources. Calling GC.Collect will thus only - if at all - release the runtime-callable-wrapper, which is a managed wrapper around the unmanaged object. The latter however still exists in memory and is likely to produce a dead-leak.
Having said this the only way to finally release a com-objects is to call Marshal.ReleaseComObject in a loop until the reference-counter is zero.
while(Marshal.ReleaseComObject(myObject) > 0);
Afterwards you may or may not call GC.Collect. However I won´t suggest to do so, as the garbage-collector knows best when to release a mananed object. Forcing it to do so will at best work as expected, at worst however only mke your code slow without having any positive effect. GC is un-determinsitc process, you can´t really influence it.
Best way to do is using ComReleaser or Marshal.ReleaseComObject methods. Calling GC.Collect method too much will cause to slow down of your application. Let GC do its job when needed.
Try this,
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(oApp);
oApp = null; // Set each COM Object to null
//
// After all of the COM objects have been released and set to null, do the following:
GC.Collect(); // Start .NET CLR Garbage Collection
GC.WaitForPendingFinalizers(); // Wait for Garbage Collection to finish
If object implement finalizer, call gc means put this object reference in finalization queue, means it doesnt release immediately.
Related
I was wondering what are the best practices when creating objects, performing LINQ in C#. For instance, I realize that when I open up a connection using LINQ I should be putting the model object into a using statement like this:
using(var ctx = new mymodel())
{
}
Now, what about the objects that are EF classes?
They do not implement IDisposable interface thus I can't do something like this when creating an object like this for example:
using(var user = new Users())
{
}
But when an action is called like this:
public ActionResult InsertUser()
{
var user = new Users();
}
I have no clear picture what happens with this object once the insert into the db is finished. Does this object stays allocated in memory or it gets released? If not, what are best practices to release memory once they aren't needed anymore..?
On the other there are static variables as well...
So to sum things up, my questions are:
what are best practices to release memory when creating instances of an object of a class?
Is implementation of IDisposable interface on every class that I have good choice?
When a static variable is created in .NET MVC, what is the best way to release memory taken by this kind of variables?
Same question goes for Session object?
P.S. Guys, I'd really appreciate if all of you that are reading this to post your opinion or post some useful links for some documentations/blog posts so that I can expand my horizons =)
Before you do any performance tweaks I highly recommend to run a memory profiler (for example JetBrains dotMemory, but there are others) and find out the actual source of the problem. Without information from profiler, your optimisations will be like sticking your finger at a sky and shouting "Rainbow!" i.e. useless at best, harmful at worst.
Also after identifying issues with profiler, but before starting changing your code, I recommend reading about how Garbage Collection works in .Net. Here are some references to get you started:
MSDN Garbage Collection
MSDN Garbage Collector Basics and Performance Hints
.Net Garbage Collection in depth
Here are some links to answer your questions:
IDisposable vs Garbage Collector
Static vs Garbage Collector: 1, 2
Session and Garbage Collection
In response to comment.
When you create regular .NET object, .NET runtime knows everything about it, because runtime created it. It knows where in memory it's located, when it is no longer needed and so on. When it is no longer needed - runtime will reclaim its memory. That is managed (by runtime) object. You should not care about memory management for such objects.
Now take for example file handle. When you open file in .NET, it will delegate this operation to OS (for example, Windows). This resource is not managed by runtime, it is managed by OS. So, it is unmanaged resource. So author of .NET library code which works with files (I mean person who created FileStream and similar classes, not uses them) should manually write code to release such resource (close file handle in this case). By convention, author will use IDisposable.Dispose method for code that releases such resources AND ensures that when underlying object (like FileStream) will be collected by GC - unmanaged resources will also be released. So even if you forget to call Dispose - file handle will be closed when GC collects FileStream (not that it will happen not magically but because code author explicitly made it happen like this). But that might happen any time in the future, you don't want to leave file handle open for undetermined time - so you always call Dispose yourself when you are done with IDisposable object.
The same applies to most unmanaged resources. For example, database connection is managed by database, and you don't want to leave database connection open for undetermined time, so you call Dispose and explicitly notify database to close it (and even if you don't call it - author of .NET database code took care to do that when object is collected). EF context wraps database connection, so it also implements IDisposable. If you don't want to leave connection open until context will be collected (and you sure don't want that) - you call Dispose yourself.
Or suppose you write your own code to work with images using ImageMagick. That is C library, so runtime has no idea about its inner workings. You ask library to allocate memory for your image, but .NET cannot reclaim such memory - it does not manage it, it is managed by ImageMagick C library. So you implement IDisposable and tell ImageMagick to release memory in that method.
In the case of var user = new Users(); which does not implement IDisposable, and any object for that matter which is not disposed, will only be guaranteed to exist where there is an active reference to this object; after which it will qualify for disposal the next time the Garbage Collector (GC) tries to free up memory.
In your example above, as soon as this leaves the method InsertUser() it will no longer have any references pointing to it, and therefore will be up for garbage collection.
If however a reference exists, such as in the following code, the object will not be disposed until the reference is cleared, or the containing class is disposed.
private User _insertedUser;
public ActionResult InsertUser()
{
_insertedUser = new Users();
}
The garbage collector fires when the application needs to release some memory. When it fires, it performs several sweeps on objects within memory to tell if any references exist. It first scans every object which has been newly created since the last time the GC was called. Objects which survive this sweep are promoted a generation. If after the sweep more memory is still required, then a 2nd generation sweep is performed. This will scan every object which has survived a single garbage collection to see if it can now be freed. Again if an object survives, it moves up another generation, with 3 generation sweeps in total.
This method helps the GC perform memory management while limiting the high costs involved (newer objects are more likely to be available for release). As GC has a high cost associated to it, it's better if objects are disposed of through user's code to help limit the number of times the GC is called.
tldr;
Dispose objects if they implement IDisposable; an easy way is to
surround it by a 'using'.
If an object cannot be disposed, and it is no longer required, make sure all references to the object is cleared (especially references outside of a method which the object was created).
This enables the GC to release it from memory.
This question already has an answer here:
When is an object subject to garbage collection?
(1 answer)
Closed 7 years ago.
I'm still somewhat confused by C#'s garbage collector. Documentation states that an object is garbage collected when there are no more references pointing to it.
So my question is, why is this object not immediately garbage collected? There is no reference to it. Let's assume that the background class itself creates no references either.
public void StartGame() {
// the background instance is created, but no reference is kept:
new Background("landscape.png", speed);
}
What you are describing is of more resemblance to memory management through reference counting, that checks whether to free the memory when the reference counter is accessed, typically when the referencing object is constructed or destroyed.
Garbage collection is a slightly different concept. Directly freeing the objects in GC-powered environments is typically not allowed or not recommended. Instead, a garbage collector is run occasionally (the runtime decides when to do that), that finds all the no longer referenced objects and frees the memory taken by these. The point is, you should not bother (because you can do nothing about it) when exactly it's going to happen.
The garbage collector is like the garbage truck that drives around your neighborhood. You can't will it to pick up your trash once you put it on the side of the street. You have to wait for it to come on its own terms.
Garbage collectors are theoretically really simple: stop the world, determine what's no longer being used, collect, resume the world. But because this takes time, developers use complex algorithms to decide when the collector kicks in and what it collects, to make your program run as smoothly as possible. Some garbage is usually not a problem that affects your program.
If you are expecting your object to be collected as soon as it goes out of scope, and you're probably using a finalizer to test this. Don't! Instead, implement IDisposable and call the Dispose method yourself, as soon as you're done with it. You can't rely on the garbage collector to collect your object at any time, if ever. That's why the BCL I/O classes all implement IDisposable for flushing streams, closing connections and cleaning up memory.
Or if you want to keep your object around, you need to keep an (indirect) reference to it. The object might just be collected on the next garbage collection cycle.
Well, there's one not recommended way to force the garbage truck to collect your garbage, using GC.Collect:
GC.Collect();
This will temporary stop your program to collect all garbage. However, this might still not clear your Background object while it's living on the stack or some other place. You can't predict where the runtime will put your object and when it will release it, so be sure to at least exit the method that created the object before testing whether it is collected using GC.Collect.
The garbage collector in C# is invoked only at certain moments and is generational. This means that an object that has no reference at the first pass of the GC will be upgraded by 1 generation. The lowest generation is garbage collected way more often than the rest.
You may read this article to understand more: https://msdn.microsoft.com/en-us/library/ee787088%28v=vs.110%29.aspx
You may alternatively call the GC.Collect method, but this is not recommended as .NET is pretty much able to handle its own memory and invoking this method kinda defeats the whole purpose.
Well if you want to check if an object will be garbage collected you can do the following to test in your code if it is eligible for collection by doing the following.
Modify your method a little for testing alternatively you could make reference a field that is set in the StartGame Method.
public void StartGame(out WeakReference reference)
{
reference = new WeakReference(new Background("landscape.png", speed));
}
After your method is called you can do the following to see if it is eligible for collection.
WeakReference reference;
StartGame(out reference);
GC.Collect();
GC.WaitForPendingFinalizers();
if (reference.IsAlive)
{
Console.WriteLine("Background is not eligible for collection");
}
This is only for testing, you shouldn't be calling the Garbage collector otherwise.
Sometimes when I end the application and it tries to release some COM objects, I receive a warning in the debugger:
RaceOnRCWCleanUp was detected
If I write a class which uses COM objects, do I need to implement IDisposable and call Marshal.FinalReleaseComObject on them in IDisposable.Dispose to properly release them?
If Dispose is not called manually then, do I still need to release them in the finalizer or will the GC release them automatically? Now I call Dispose(false) in the finalizer but I wonder if this is correct.
The COM object I use also have an event handler which the class listens to. Apparently the event is raised on another thread, so how do I correctly handle it if it is fired when disposing the class?
First - you never have to call Marshal.ReleaseComObject(...) or Marshal.FinalReleaseComObject(...) when doing Excel interop. It is a confusing anti-pattern, but any information about this, including from Microsoft, that indicates you have to manually release COM references from .NET is incorrect. The fact is that the .NET runtime and garbage collector correctly keep track of and clean up COM references.
Second, if you want to ensure that the COM references to an out-of-process COM object is cleaned up when your process ends (so that the Excel process will close), you need to ensure that the Garbage Collector runs. You do this correctly with calls to GC.Collect() and GC.WaitForPendingFinalizers(). Calling twice is safe, end ensures that cycles are definitely cleaned up too.
Third, when running under the debugger, local references will be artificially kept alive until the end of the method (so that local variable inspection works). So a GC.Collect() calls are not effective for cleaning object like rng.Cells from the same method. You should split the code doing the COM interop from the GC cleanup into separate methods.
The general pattern would be:
Sub WrapperThatCleansUp()
' NOTE: Don't call Excel objects in here...
' Debugger would keep alive until end, preventing GC cleanup
' Call a separate function that talks to Excel
DoTheWork()
' Now Let the GC clean up (twice, to clean up cycles too)
GC.Collect()
GC.WaitForPendingFinalizers()
GC.Collect()
GC.WaitForPendingFinalizers()
End Sub
Sub DoTheWork()
Dim app As New Microsoft.Office.Interop.Excel.Application
Dim book As Microsoft.Office.Interop.Excel.Workbook = app.Workbooks.Add()
Dim worksheet As Microsoft.Office.Interop.Excel.Worksheet = book.Worksheets("Sheet1")
app.Visible = True
For i As Integer = 1 To 10
worksheet.Cells.Range("A" & i).Value = "Hello"
Next
book.Save()
book.Close()
app.Quit()
' NOTE: No calls the Marshal.ReleaseComObject() are ever needed
End Sub
There is a lot of false information and confusion about this issue, including many posts on MSDN and on StackOverflow.
What finally convinced me to have a closer look and figure out the right advice was this post https://blogs.msdn.microsoft.com/visualstudio/2010/03/01/marshal-releasecomobject-considered-dangerous/ together with finding the issue with references kept alive under the debugger on some StackOverflow answer.
Based on my experience using different COM objects (in-process or out-of-process) I would suggest one Marshal.ReleaseComObject per one COM/ .NET boundary crossing (if for an instance you reference COM object in order to retrieve another COM reference).
I have run into many issues just because I decided to postpone COM interop cleanup to GC.
Also please notice, I never use Marshal.FinalReleaseComObject - some COM objects are singletons and it doesn't work well with such objects.
Doing anything in managed objects inside finalizer (or Dispose(false) from the well-known IDisposable implementation) is forbidden. You must not rely on any .NET object reference in the finalizer. You can release IntPtr, but not COM object as it could already be clean up.
There's an article here on that: http://www.codeproject.com/Tips/235230/Proper-Way-of-Releasing-COM-Objects-in-NET
In a nutshell:
1) Declare & instantiate COM objects at the last moment possible.
2) ReleaseComObject(obj) for ALL objects, at the soonest moment possible.
3) Always ReleaseComObject in the opposite order of creation.
4) NEVER call GC.Collect() except when required for debugging.
Until GC naturally occurs, the com reference will not be fully released. This is why so many people need to force object destruction using FinalReleaseComObject() and GC.Collect(). Both are required for dirty Interop code.
Dispose is NOT automatically called by the GC. When an object is being disposed, the destructor is called (in a different thread). This is usually where you could release any unmanaged memory, or com references.
Destructors: http://msdn.microsoft.com/en-us/library/66x5fx1b.aspx
... when your application encapsulates unmanaged resources such as windows, files, and network connections, you should use destructors to free those resources. When the object is eligible for destruction, the garbage collector runs the Finalize method of the object.
What happens if I don't call Dispose on the pen object in this code snippet?
private void panel_Paint(object sender, PaintEventArgs e)
{
var pen = Pen(Color.White, 1);
//Do some drawing
}
A couple of corrections should be made here:
Regarding the answer from Phil Devaney:
"...Calling Dispose allows you to do deterministic cleanup and is highly recommended."
Actually, calling Dispose() does not deterministically cause a GC collection in .NET - i.e. it does NOT trigger a GC immediately just because you called Dispose(). It only indirectly signals to the GC that the object can be cleaned up during the next GC (for the Generation that the object lives in). In other words, if the object lives in Gen 1 then it wouldn't be disposed of until a Gen 1 collection takes place. One of the only ways (though not the only) that you can programmatically and deterministically cause the GC to perform a collection is by calling GC.Collect(). However, doing so is not recommended since the GC "tunes" itself during runtime by collecting metrics about your memory allocations during runtime for your app. Calling GC.Collect() dumps those metrics and causes the GC to start its "tuning" all over again.
Regarding the answer:
IDisposable is for disposing unmanaged resources. This is the pattern in .NET.
This is incomplete. As the GC is non-deterministic, the Dispose Pattern, (How to properly implement the Dispose pattern), is available so that you can release the resources you are using - managed or unmanaged. It has nothing to do with what kind of resources you are releasing. The need for implementing a Finalizer does have to do with what kind of resources you are using - i.e. ONLY implement one if you have non-finalizable (i.e. native) resources. Maybe you are confusing the two. BTW, you should avoid implementing a Finalizer by using the SafeHandle class instead which wraps native resources which are marshaled via P/Invoke or COM Interop. If you do end up implementing a Finalizer, you should always implement the Dispose Pattern.
One critical note which I haven't seen anyone mention yet is that if disposable object is created and it has a Finalizer (and you never really know whether they do - and you certainly shouldn't make any assumptions about that), then it will get sent directly to the Finalization Queue and live for at least 1 extra GC collection.
If GC.SuppressFinalize() is not ultimately called, then the finalizer for the object will be called on the next GC. Note that a proper implementation of the Dispose pattern should call GC.SuppressFinalize(). Thus, if you call Dispose() on the object, and it has implemented the pattern properly, you will avoid execution of the Finalizer. If you don't call Dispose() on an object which has a finalizer, the object will have its Finalizer executed by the GC on the next collection. Why is this bad? The Finalizer thread in the CLR up to and including .NET 4.6 is single-threaded. Imagine what happens if you increase the burden on this thread - your app performance goes to you know where.
Calling Dispose on an object provides for the following:
reduce strain on the GC for the process;
reduce the app's memory pressure;
reduce the chance of an OutOfMemoryException (OOM) if the LOH (Large Object Heap) gets fragmented and the object is on the LOH;
Keep the object out of the Finalizable and f-reachable Queues if it has a Finalizer;
Make sure your resources (managed and unmanaged) are cleaned up.
Edit:
I just noticed that the "all knowing and always correct" MSDN documentation on IDisposable (extreme sarcasm here) actually does say
The primary use of this interface is
to release unmanaged resources
As anyone should know, MSDN is far from correct, never mentions or shows 'best practices', sometimes provides examples that don't compile, etc. It is unfortunate that this is documented in those words. However, I know what they were trying to say: in a perfect world the GC will cleanup all managed resources for you (how idealistic); it will not, however cleanup unmanaged resources. This is absolutely true. That being said, life is not perfect and neither is any application. The GC will only cleanup resources that have no rooted-references. This is mostly where the problem lies.
Among about 15-20 different ways that .NET can "leak" (or not free) memory, the one that would most likely bite you if you don't call Dispose() is the failure to unregister/unhook/unwire/detach event handlers/delegates. If you create an object that has delegates wired to it and you don't call Dispose() (and don't detach the delegates yourself) on it, the GC will still see the object as having rooted references - i.e. the delegates. Thus, the GC will never collect it.
#joren's comment/question below (my reply is too long for a comment):
I have a blog post about the Dispose pattern I recommend to use - (How to properly implement the Dispose pattern). There are times when you should null out references and it never hurts to do so. Actually, doing so does do something before GC runs - it removes the rooted reference to that object. The GC later scans its collection of rooted references and collects those that do not have a rooted reference. Think of this example when it is good to do so: you have an instance of type "ClassA" - let's call it 'X'. X contains an object of type "ClassB" - let's call this 'Y'. Y implements IDisposable, thus, X should do the same to dispose of Y. Let's assume that X is in Generation 2 or the LOH and Y is in Generation 0 or 1. When Dispose() is called on X and that implementation nulls out the reference to Y, the rooted reference to Y is immediately removed. If a GC happens for Gen 0 or Gen 1, the memory/resources for Y is cleaned up but the memory/resources for X is not since X lives in Gen 2 or the LOH.
The Pen will be collected by the GC at some indeterminate point in the future, whether or not you call Dispose.
However, any unmanaged resources held by the pen (e.g., a GDI+ handle) will not be cleaned up by the GC. The GC only cleans up managed resources. Calling Pen.Dispose allows you to ensure that these unmanaged resources are cleaned up in a timely manner and that you aren't leaking resources.
Now, if the Pen has a finalizer and that finalizer cleans up the unmanaged resources then those said unmanaged resources will be cleaned up when the Pen is garbage collected. But the point is that:
You should call Dispose explicitly so that you release your unmanaged resources, and
You shouldn't need to worry about the implementation detail of if there is a finalizer and it cleans up the unmanaged resources.
Pen implements IDisposable. IDisposable is for disposing unmanaged resources. This is the pattern in .NET.
For previous comments on the this topic, please see this answer.
The underlying GDI+ pen handle will not be released until some indeterminate time in the future i.e. when the Pen object is garbage collected and the object's finalizer is called. This might not be until the process terminates, or it might be earlier, but the point is its non-deterministic. Calling Dispose allows you to do deterministic cleanup and is highly recommended.
The total amount of .Net memory in use is the .Net part + all 'external' data in use. OS objects, open files, database and network connections all take some resources that are not purely .Net objects.
Graphics uses Pens and other objects wich are actually OS objects that are 'quite' expensive to keep around. (You can swap your Pen for a 1000x1000 bitmap file). These OS objects only get removed from the OS memory once you call a specific cleanup function. The Pen and Bitmap Dispose functions do this for you immediatly when you call them.
If you don't call Dispose the garbage collector will come to clean them up 'somewhere in the future*'.
(It will actually call the destructor/finalize code that probably calls Dispose())
*on a machine with infinite memory (or more then 1GB) somewhere in the future can be very far into the future. On a machine doing nothing it can be easily longer then 30 minutes to clean up that huge bitmap or very small pen.
If you really want to know how bad it is when you don't call Dispose on graphics objects you can use the CLR Profiler, available free for the download here. In the installation folder (defaults to C:\CLRProfiler ) is CLRProfiler.doc which has a nice example of what happens when you don't call Dispose on a Brush object. It is very enlightening. The short version is that graphics objects take up a larger chunk of memory than you might expect and they can hang around for a long time unless you call Dispose on them. Once the objects are no longer in use the system will, eventually, clean them up, but that process takes up more CPU time than if you had just called Dispose when you were finished with the objects.
You may also want to read up on using IDisposable here and here.
It will keep the resources until the garbage collector cleans it up
Depends if it implements finalizer and it calls the Dispose on its finalize method. If so, handle will be released at GC.
if not, handle will stay around until process is terminated.
With graphic stuff it can be very bad.
Open the Windows Task Manager. Click "choose columns" and choose column called "GDI Objects".
If you don't dispose certain graphic objects, this number will keep raising and raising.
In older versions of Windows this can crash the whole application (limit was 10000 as far as I remember), not sure about Vista/7 though but it's still a bad thing.
the garbage collector will collect it anyway BUT it matters WHEN:
if you dont call dispose on an object that you dont use it will live longer in memory and gets promoted to higher generations meaning that collecting it has a higher cost.
in back of my mind first idea came to the surface is that this object will be disposed as soon as the method finishes execution!, i dont know where did i got this info!, is it right?
I'm new to C# and .NET, ,and have been reading around about it.
I need to know why and when do I need to release resources? Doesn't the garbage collector take care of everything? When do I need to implement IDisposable, and how is it different from destructor in C++?
Also, if my program is rather small i.e. a screensaver, do I need to care about releasing resources?
Thanks.
The garbage collector is only aware of memory. That's fine for memory, because one bit of memory is pretty much as good as any other, so long as you've got enough of it. (This is all modulo cache coherency etc.)
Now compare that with file handles. The operating system could have plenty of room to allocate more file handles - but if you've left a handle open to a particular file, no-one else will be able to open that particular file for writing. You should tell the system when you're done with a handle - usually by closing the relevant stream - as soon as you're finished, and do so in a way that closes it even if an exception is thrown. This is usually done with a using statement, which is like a try/finally with a call to Dispose in the finally block.
Destructors in C++ are very different from .NET finalizers, as C++ destructors are deterministic - they're automatically called when the relevant variable falls out of scope, for example. Finalizers are run by the garbage collector at some point after an object is no longer referenced by any "live" objects, but the timing is unpredictable. (In some rare cases, it may never happen.)
You should implement IDisposable yourself if you have any clean-up which should be done deterministically - typically that's the case if one of your instance variables also implements IDisposable. It's pretty rare these days to need to implement a finalizer yourself - you usually only need one if you have a direct hold on operating system handles, usually in the form of IntPtr; SafeHandle makes all of this a lot easier and frees you from having to write the finalizer yourself.
Basically, you need to worry about releasing resources to unmanaged code - anything outside the .NET framework. For example, a database connection or a file on the OS.
The garbage collector deals with managed code - code in the .NET framework.
Even a small application may need to release unmanaged resources, for example it may write to a local text file. When you have finished with the resource you need to ensure the object's Dispose method is called. The using statement simplifies the syntax:
using (TextWriter w = File.CreateText("test.txt"))
{
w.WriteLine("Test Line 1");
}
The TextWriter object implements the IDisposable interface so as soon as the using block is finished the Dispose method is called and the object can be garbage collected. The actual time of collection cannot be guaranteed.
If you create your own classes that need to be Disposed of properly you will need to implement the IDisposable interface and Dispose pattern yourself. On a simple application you probably won't need to do this, if you do this is a good resource.
Resources are of two kinds - managed, and unmanaged. Managed resources will be cleaned up by the garbage collector if you let it - that is, if you release any reference to the object. However, the garbage collection does not know how to release unmanaged resources that a managed object holds - file handles, and other OS resources for example.
IDisposable is best practice when there's a managed resource you want released promptly (like a database connection), and vital when there are unmanaged resources which you need to have released. The typical pattern:
public void Dispose()
protected void Dispose(bool disposing)
Lets you ensure that unmanaged resources are released whether by the Dispose method or by object finalisation.
You don't need to release memory in managed objects like strings or arrays - that is handled by the garbage collector.
You should clean up operating system resources and some unmanaged objects when you have finished using them. If you open a file you should always remember to close that file when you have finished using it. If you open a file exclusively and forget to close, the next time you try to open that file it might still be locked. If something implements IDisposable, you should definitely consider whether you need to close it properly. The documentation will usually tell you what the Dispose method does and when it should be called.
If you do forget, the garbage collector will eventually run the finalizer which should clean up the object correctly and release the unmanaged resources, but this does not happen immediately after the object becomes eligible for garbage collection, and it in fact might not run at all.
Also it is useful to know about the using statement.
The garbage collector releases MEMORY and cleans up - through disposition - elemetns it removes. BUT: IT only does so when it has memory pressure.
THis is seriously idiotic for ressources whree I may want to explicitely release them. Save to file, for example, is supposed to: Open the file, write out the data and - close the file, so it can be copied away by the user if he wants, WITHOUT waiting for the GC to come around and release the memory for the file object, which may not happen for hours.
You only need to worry about precious resources. Most objects you create while programming do not fit into this category. As you say, the garbage collector will take care of these.
What you do need to be mindful of is objects that implement IDisposable, which is an indication that the resources it owns are precious and should not wait for the finalizer thread to be cleaned up. The only time you would need to implement IDisposable is on classes that own a) objects that implement IDisposable (such as a file stream), or b) unmanaged resources.