[Mono-list] issue with pinvoke & memory leak

Jonathan Pryor jonpryor@vt.edu
Wed, 02 Jun 2004 20:30:07 -0400

On Wed, 2004-06-02 at 08:09, Chris Turchin wrote:
> Hi Ravindra,
> again, thanks for the tip. I checked and the finalizer  was there only it was
> not getting called (or not often enough) - i added a GC.Collect() call to my
> test class and it worked.

Don't call GC.Collect().  Please.  Under .NET, manually calling
GC.Collect will throw off the algorithms that govern when .NET normally
collects memory, which can negatively impact performance.

Though relying on the finalizer will already impact performance, as
finalizers aren't run until after the object graph has been promoted a
generation.  For example, if it's a Generation 0 object that has a
finalizer and is collected, it's promoted to Generation 1 and the
finalizer isn't run until the *next* Generation 1 collection.  Which is
less frequent than Generation 0 collections.

If your object is promoted to Generation 2, it'll hang around awhile.

Result: you may have lots of unmanaged memory "leaking", because it's
waiting for the collector to finalize the object...  (Imagine that you
have a 10 MB image opened in unmanaged memory and held open by a managed
handle.  It may be awhile before that memory is freed.)  Assuming, of
course, that the finalizer is *ever* executed, and there is no guarantee
that it will be.

In short, finalizers are nice as a fall back, but DO NOT rely on them. 
You have to remember to dispose of your resources in a timely basis.

Consequently, you should instead do:

	FreeImage img = GetImage ();
	FreeImage img2 = img.Resample (...);
	img.Dispose ();
	img = img2;

That's assuming that you really need to do the aliasing.  If you don't
need to keep re-using the "img" variable, you could use the "using"

	using (FreeImage img = GetMyImage()) {
		using (FreeImage img2 = img.Resample (...)) {
			// operate on img2

This will ensure that your unmanaged memory is freed in a timely manner.

Mono doesn't currently use a generational collector, so this currently
isn't as much of an issue.

> > > Actaully, would this same
> > > issue be a problem for all IDisposable objects in managed code (maybe I am not
> > > allowed to do this at all with IDisposable objects) or is this unique to the
> > > managed/unmanaged code in question here?

This is true for all IDisposable objects.  If the object doesn't have a
finalizer, then nothing will be invoked when the object is collected. 
Either invoke Dispose() directly, or use a using block so that Dispose()
is called for you.

This should be done for all resources -- unmanaged memory, database
locks, file handles, monitors... Everything that may be limited and
needs to be released in a timely manner to avoid performance issues.

Finalizers can help in freeing these resources, but they're no
guarantee.  Use them as a fall-back ONLY.

 - Jon