GC and dispose

P

Petros Amiridis

Hi,

When I create an instance of one of my classes inside a method, does it
make a difference if I call its Dispose method?

public class Foo : IDisposable
{
public Foo
{
}

public Foo anotherFoo = null;

public void Dispose()
{
}
}

Given the class above is there a difference between the two examples
below? When will the GC give back the memory? Does ExampleA confuse GC
when it comes to tracking and releasing foo2?

public void ExampleA()
{
Foo foo1 = new Foo();
Foo foo2 = new Foo();
Foo foo3 = new Foo();
foo1.anotherFoo = foo2;
foo1.anotherFoo = foo3;
}

public void ExampleB()
{
Foo foo1 = new Foo();
Foo foo2 = new Foo();
Foo foo3 = new Foo();
foo1.anotherFoo = foo2;
foo2.Dispose();
foo1.anotherFoo = foo3;
foo1.Dispose();
foo3.Dispose();
}

Thanx,
 
P

Phil Wilson

You example doesn't help because there's nothing in Foo that needs
disposing, and Dispose isn't related to GC as you seem to be assuming.
 
M

Markus Stoeger

Petros said:
Hi,

When I create an instance of one of my classes inside a method, does it
make a difference if I call its Dispose method?

Sure. If an object is IDisposable, you have to call Dispose on it sooner
or later (not necessarly in the same method). If you don't, you might
create a resource leak which will make your program randomly throw
OutOfMemoryExceptions at your users.

Calling Dispose has no effect on when the memory of your managed objects
will get freed by the GC though.

hth,
Max
 
P

Petros Amiridis

Phil said:
You example doesn't help because there's nothing in Foo that needs
disposing, and Dispose isn't related to GC as you seem to be assuming.

What if it does have something that needs disposing? An if Dispose
doesn't have anything to do with GC, could tell me briefly what are
good practices that can make your application consume less memory?

I've noticed that my application starts and consumes several times more
memory than equivalent native applications. The problem is that
whenever I open/close windows in my application, the memory that it
occupies grows more. I wait and nothing happens. Shouldn't GC release
the memory as long as an object is not referenced any more?

Thank you,
 
P

Petros Amiridis

Markus said:
[...]
Calling Dispose has no effect on when the memory of your managed
objects will get freed by the GC though.

What should I be careful of not doing in order to avoid resource leaks
apart from calling Dispose?

Thank you,
 
D

Daniel Billingsley

Petros Amiridis said:
What if it does have something that needs disposing? An if Dispose
doesn't have anything to do with GC, could tell me briefly what are
good practices that can make your application consume less memory?

I've noticed that my application starts and consumes several times more
memory than equivalent native applications. The problem is that
whenever I open/close windows in my application, the memory that it
occupies grows more. I wait and nothing happens. Shouldn't GC release
the memory as long as an object is not referenced any more?

I think to some degree it's a waste of time trying to help GC do its job
better in the way it seems you're thinking, as it is a fairly indeterminate
mechanism in the first place. It is somewhat important in some cases to
help GC have less to do though, because it can become a performance issue.
For example, using lots of stringbuilders for what are really just simple
concatenations. But that really manifests itself as a performance issue
more than a memory one in my testing.

As somebody else said, the Dispose pattern is for making sure UNMANAGED
resources inside your classes get cleaned up. That is, making sure there
are no memory leaks in things that GC doesn't try to deal with. That's why
the statement "Dispose doesn't have anything to do with GC".

I have no idea what "several times more memory than equivalent native
applications" means. Equivalent how -Functionality? Code? That's such a
nebulous comparison I don't think I'd spend much time worrying about that
either.

Is there a specific problem you're having or specific reason you think you
may have a memory leak in the application?
 
P

Petros Amiridis

Daniel said:
As somebody else said, the Dispose pattern is for making sure
UNMANAGED resources inside your classes get cleaned up.

This more clear to me now. I really thought Dispose would help GC.
Thank you and the rest for the clarification.
I have no idea what "several times more memory than equivalent native
applications" means. Equivalent how -Functionality? Code? That's
such a nebulous comparison I don't think I'd spend much time worrying
about that either.

I admit that is based entirely on my experience and not in crystal
clear measurements. My heuristics mechanism takes under consideration
all the factors: Functionality, Number of Forms etc. Sure, I might be
inaccurate in my comparison, but the numbers are very different between
native and managed applications. My native applications take memory and
give it back as soon as I don't need the resources I've used. In
managed applications, the memory keeps being eaten until the GC decides
it is time to collect. The problem with this is that my customer does
run more applications than one. The customer also uses his computer for
other puproses, so she runs third party applications too. So to
conclude, I don't have a specific problem, other than the need to tell
my customers to add some more memory to their machines. Maybe it is not
a leak, it is just taking the GC a long time to collect back the memory.
 
C

Carl Daniel [VC++ MVP]

Daniel said:
As somebody else said, the Dispose pattern is for making sure
UNMANAGED resources inside your classes get cleaned up.

I would take exception with that statement. Dispose is for making sure
resources that are not simply managed heap allocations get cleaned up.
Unmanaged resources such as handles or Win32 memory allocations are fine
examples of such, but there are certainly managed examples as well. A
common example of a managed resource that needs to be explicitly cleaned up
is a callback (delegate) which can keep an object alive long after you're
done with it, consuming both memory and CPU resources making callbacks to an
object that no one cares about anymore.

-cd
 
S

Simon Hart

Agree. We often use the Dispose (IDisposable) for cleaning managed resources
as well as unmanaged. We tend to use the Finalizer (Destructor) for
unmanaged deallocation.

Regards
Simon.
 
S

Simon Hart

Petros Amiridis said:
This more clear to me now. I really thought Dispose would help GC.
Thank you and the rest for the clarification.


I admit that is based entirely on my experience and not in crystal
clear measurements. My heuristics mechanism takes under consideration
all the factors: Functionality, Number of Forms etc. Sure, I might be
inaccurate in my comparison, but the numbers are very different between
native and managed applications. My native applications take memory and
give it back as soon as I don't need the resources I've used. In
managed applications, the memory keeps being eaten until the GC decides
it is time to collect. The problem with this is that my customer does
run more applications than one. The customer also uses his computer for
other puproses, so she runs third party applications too. So to
conclude, I don't have a specific problem, other than the need to tell
my customers to add some more memory to their machines. Maybe it is not
a leak, it is just taking the GC a long time to collect back the memory.

The GC will kick in when required - for example memory is low, but it will
only release managed
objects if: A. They have been marked to be Diposed or B. They are
unreachable on the heap.

Regards
Simon.
 
M

Markus Stoeger

Simon said:
Agree. We often use the Dispose (IDisposable) for cleaning managed resources
as well as unmanaged. We tend to use the Finalizer (Destructor) for
unmanaged deallocation.

Could you explain why you prefer the finalizer instead of a manual call
to Dispose for freeing unmanaged resources?

As far as I know you can never tell when the GC will finalize an object.
So it might keep floating around forever as well. Which means that you
could run out of handles for example.

Maybe I've misunderstood something behind GC and the finalizer?.

Max
 
?

=?ISO-8859-1?Q?G=F6ran_Andersson?=

Simon said:
The GC will kick in when required - for example memory is low, but it will
only release managed
objects if: A. They have been marked to be Diposed or B. They are
unreachable on the heap.

Regards
Simon.

Actually only condition B is relevant.

You can't mark objects to be collected, the only way to make them
collectable is to make them unreachable.
 
?

=?ISO-8859-1?Q?G=F6ran_Andersson?=

Markus said:
Could you explain why you prefer the finalizer instead of a manual call
to Dispose for freeing unmanaged resources?

As far as I know you can never tell when the GC will finalize an object.
So it might keep floating around forever as well. Which means that you
could run out of handles for example.

Maybe I've misunderstood something behind GC and the finalizer?.

Max

No, that is correct.

The Dispose method should be used to free resources. The finalizer
should call Dispose as a backup if the program failed to call it.
 
C

Carl Daniel [VC++ MVP]

Göran Andersson said:
No, that is correct.

The Dispose method should be used to free resources. The finalizer should
call Dispose as a backup if the program failed to call it.

More specifically, the recommended structure for Finalize and Dispose is:

class C : IDisposable
{
void IDisposable.Dispose()
{
Dispose(true);
}

~C()
{
Dispose(false)
}

protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// free managed resources here

GC.SuppressFinalize(this);
}

// free unmanaged resources here
}
}

The C++/CLI compiler automatically implements this structure, while in C#/VB
you have to do it yourself. Current versions of FxCop will raise warnings
if your class implements IDisposable but doesn't do it this way.

Of course, if your class cannot be derived from, then there's no need for
Dispose(bool) to be virtual.

-cd

-cd
 
D

Daniel Billingsley

Let's talk. :)

This would apply to simple events then, since they are really just fancy
delegates. I've never heard of anybody talk of implementing Dispose in a
class just because it raises events. Should we?

Let's lay some groundwork. The problem with keeping an object "alive" is
because something references it, not the other way around. Let's imagine an
ObjectB that has a reference to ObjectA because ObjectA was previously
hooked to an event in ObjectB (creating a delegate reference as you
describe).

Now ObjectB goes out of scope. It obviously isn't "making callbacks" any
more, so there's no reason for it to have Dispose() for that reason. The
problem with keeping ObjectA "alive" consuming memory is only temporary
until GC does its thing with ObjectB. That's the whole point of GC.

But, for you GC gurus - isn't GC smart enough to know that the only
reference to ObjectA is from a "dead" ObjectB and therefore consider ObjectA
itself eligible for collection?
 
M

Michael D. Ober

I don't know about the C# environment, but VB 2005 can be configured to
insert the dispose pattern, as well as other implementation patterns, when
you add the Implements clause to the class definition and press return.

Mike Ober.
 
M

Michael D. Ober

Daniel Billingsley said:
Let's talk. :)

This would apply to simple events then, since they are really just fancy
delegates. I've never heard of anybody talk of implementing Dispose in a
class just because it raises events. Should we?

Let's lay some groundwork. The problem with keeping an object "alive" is
because something references it, not the other way around. Let's imagine an
ObjectB that has a reference to ObjectA because ObjectA was previously
hooked to an event in ObjectB (creating a delegate reference as you
describe).

Now ObjectB goes out of scope. It obviously isn't "making callbacks" any
more, so there's no reason for it to have Dispose() for that reason. The
problem with keeping ObjectA "alive" consuming memory is only temporary
until GC does its thing with ObjectB. That's the whole point of GC.

But, for you GC gurus - isn't GC smart enough to know that the only
reference to ObjectA is from a "dead" ObjectB and therefore consider ObjectA
itself eligible for collection?

Yes it is. I have explicitely tested this scenerio.

Mike.
 
?

=?ISO-8859-1?Q?G=F6ran_Andersson?=

Yes, it is.

When the GC does it's thing, it starts out by considering all objects as
collectable, then looks for references to find out which objects are not
collectable. As it doesn't look in collectable objects for references,
it still sees that unreachable objects are collectable although they
reference each other.

"Every object charged with being collectable will be presumed guilty
until proved reachable." ;)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top