By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
454,671 Members | 1,413 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 454,671 IT Pros & Developers. It's quick & easy.

GC! Why not ref count?

P: n/a
C++ is a great language. You can instantiate an object on
the stack, grab whatever resources you need, and free them
in the destructor. Object destructors are called in the
correct order (the reverse order), and are guaranteed to
be called when the object goes out of scope.

Unfortunately this is not so in C# (or Java) since you
cannot tell when the Garbage Collector will run. In C#
you can call Dispose (assuming it's implemented), but you
you still might have several references to a invalid
object floating around. Alternatively you can take
advantage of the using statement IF the class implements
IDispose, however this can become unwieldy if you need
several nested using blocks.

So why did the developers of C# (and Java) not reference
count objects? Surely it would have been a simple matter
to implement reference counting, and guarantee calling the
destructor when the last reference to an object was set to
null. The GC could still run periodically to clean up and
re-organize memory, but at least the resource cleanup
problem would have been solved.

What am I missing?

Thanks.
Nov 13 '05 #1
Share this Question
Share on Google+
14 Replies


P: n/a

"Ron James" <Ro*******@noapsm.rojacs.com> wrote in message
news:09****************************@phx.gbl...
C++ is a great language. You can instantiate an object on
the stack, grab whatever resources you need, and free them
in the destructor. Object destructors are called in the
correct order (the reverse order), and are guaranteed to
be called when the object goes out of scope.
That is only true of stack allocated objects. If you have heap allocated
objects then you are responsible for calling delete when done with the
object. This causes many problems... Memory leaks (no delete), calling
delete on invalid pointer (crash!), calling delete to an object that is
still needed (crash! or corruption). GC is popular because it frees the
programmer from the need to explicitly manage memory allocation.
Unfortunately this is not so in C# (or Java) since you
cannot tell when the Garbage Collector will run. In C#
you can call Dispose (assuming it's implemented), but you
you still might have several references to a invalid
object floating around. Alternatively you can take
advantage of the using statement IF the class implements
IDispose, however this can become unwieldy if you need
several nested using blocks.
You are not understanding the Dispose pattern. Dispose is only implemented
for classes that hold references to unmanaged resources - such as file
handles, window handles, database connections, socket handles, etc.) These
are limmited OS resources, that need to be returned as soon as possible. In
other words, you don't want to wait around for your finalize method to be
called for those resources to be released. Not only that, overriding
finalize has negative consequences for GC performance - because it requires
to GC's to completely destroy the object. So do get around that, MS has
provided a pattern based approach for dealing with these types of resources
via the IDisposable interface. Generally all necessary clean up is called
from both the finalize method as well as the Dispose method, with the added
step that System.GC.SuppressFinalize will be called in the Dispose method.
This lets you insure that unmanaged resources are cleaned up eventually -
even if a developer forgets to call Dispose, but rewards those that do
remember by removing the performance hit at GC time.
So why did the developers of C# (and Java) not reference
count objects? Surely it would have been a simple matter
to implement reference counting, and guarantee calling the
destructor when the last reference to an object was set to
null. The GC could still run periodically to clean up and
re-organize memory, but at least the resource cleanup
problem would have been solved.


Mostly because ref counting has several side affects.
1) Performance - generally reference counting schemes do not perform as well
as GC schemes as implemented in Java and .NET for the simple fact that they
have to be thread safe, which mean syncronization must be in place.

2) Referecne counting is easy to screw up. Often they are implmented using
methods that increment or decrement the Ref count and it is up to the
developer or the runtime to make sure these are called in a balanced manner.

3) Ref counting doesn't deal with object cycles well (aka circular
references). This is one of the major source of memory leaks in VB.CLASSIC.

The fact is that the style of GC used by C#/Java is much more effiecient and
less prone to error then reference counting...

Tom Shelton
Nov 13 '05 #2

P: n/a
Ron,

This was debated *a lot* in the early days of .NET. I suggest you read
this piece to get an idea of how the designers were thinking

http://discuss.develop.com/archives/...OTNET&P=R28572

Mattias

--
Mattias Sjögren [MVP] mattias @ mvps.org
http://www.msjogren.net/dotnet/
Please reply only to the newsgroup.
Nov 13 '05 #3

P: n/a
Check out some of the other OO languages which do handle large numbers
of objects and deal with memory management. You'll see they all
abandoned reference counting (mark and sweep) for two reasons

1 - It's very expensive. You have to visit every node every pass. At
least 2 times (mark and sweep pass) and once again if you're actually
using it. Slooooooowwwwwwwwwwwwwww.

2 - It doesn't exploit the situation. Most objects are either created
and blown away before they've drawn two breaths, or they stay around
like my old socks. Smalltalk and CLOS both headed for generation
scavenging in the early 90's, but I expect the fields become even more
abstruse at this point.
Nov 13 '05 #4

P: n/a
"Tom Shelton" <ts****@yahoo.com> wrote in message news:<#d**************@TK2MSFTNGP12.phx.gbl>...
"Ron James" <Ro*******@noapsm.rojacs.com> wrote in message
news:09****************************@phx.gbl... [snip]
Unfortunately this is not so in C# (or Java) since you
cannot tell when the Garbage Collector will run. In C#
you can call Dispose (assuming it's implemented), but you
you still might have several references to a invalid
object floating around. Alternatively you can take
advantage of the using statement IF the class implements
IDispose, however this can become unwieldy if you need
several nested using blocks.


You are not understanding the Dispose pattern. Dispose is only implemented
for classes that hold references to unmanaged resources - such as file
handles, window handles, database connections, socket handles, etc.)


Ahem, you don't seem to understand it fully either or I have
misunderstood it completely ;-). Whether a class implements
IDisposable or not has nothing to do with the class having *unmanaged*
resources. When a class implements IDisposable then it provides the
outside world with a means to deterministically clean up resources -
nothing more, nothing less. Some of these resources *might* be
unmanaged of course, but only might. For example, have a look at
System.IO.BinaryWriter. This class implements IDisposable but does
itself not have unmanaged resources. Among other (managed) data
members it owns a System.IO.Stream object and a buffer. The Dispose()
method is only there to flush that buffer and then close the
underlying stream. If you fail to call Dispose() before letting the
BinaryWriter object go out of scope you are guaranteed to lose some
data, because BinaryWriter does not have a finalizer. This is
intentional as you cannot access other managed objects (here: the
stream) in a finalizer. Why? Because the order of finalization of no
longer reachable objects is not defined. You might get lucky sometimes
but it's definitely not guaranteed to work.

Except for a few *very* esotheric cases, finalizers are only good to
clean up _unmanaged_ resources. E.g. if you have a look at
mscorlib.dll with ILDASM, you'll find that classes which have
unmanaged resources invariably also implement a finalizer. To avoid
code duplication, some of them combine finalization with disposal in a
pattern (the one with the virtual Dispose( bool dispose ) function).
Dispose() can be used to clean up managed *and* unmanaged resources
while Finalize() only releases the unmanaged resources.
These
are limmited OS resources, that need to be returned as soon as possible. In
other words, you don't want to wait around for your finalize method to be
called for those resources to be released.
True, but only part of the story, see above.
via the IDisposable interface. Generally all necessary clean up is called
from both the finalize method as well as the Dispose method, with the added
Finalize() only cleans up unmanaged resources, see above.

[snip] 2) Referecne counting is easy to screw up. Often they are implmented using
methods that increment or decrement the Ref count and it is up to the
developer or the runtime to make sure these are called in a balanced manner.


Yep, but it's also quite easy to get right. E.g. see

http://www.boost.org/libs/smart_ptr/shared_ptr.htm

Regards,

Andreas
Nov 13 '05 #5

P: n/a
"Andreas Huber" <ah****@gmx.net> wrote in message
news:3e**************************@posting.google.c om...
Finalize() only cleans up unmanaged resources, see above.


QUIZ : Why then Regex has a Finalizer ?
there is a ref counting mechanism under it
this approach is either extremely sophisticated (and i cannot understand
it) or extremely stupid
Nov 13 '05 #6

P: n/a
ncaHammer wrote:
"Andreas Huber" <ah****@gmx.net> wrote in message
news:3e**************************@posting.google.c om...
Finalize() only cleans up unmanaged resources, see above.


QUIZ : Why then Regex has a Finalizer ?
there is a ref counting mechanism under it
this approach is either extremely sophisticated (and i cannot
understand it) or extremely stupid


I've just had a look at the the class with ILDASM. This seems to be one of
the esotheric uses of finalizers I was talking about in the lines above the
one you quoted. Regex has a static HashTable field called "livecode". I'm
far from being proficient with MSIL so I don't really understand the code,
but it seems that all constructors put something in it while the destructor
(Finalizer) removes something from it. This is safe because the static data
member is supposed (guaranteed?) to exist at least as long as Regex objects.

Definitely, there are other uses for finalizers than just cleaning up
unmanaged resources. However, as I explained, you're quite limited in what
you can safely do with them. The other non-cleanup use I know uses
finalizers to implement an object pool and - no wonder - also only accesses
static data members in the finalizer.

I bet you won't find many such finalizers in the .NET framework.

Regards,

Andreas

Nov 13 '05 #7

P: n/a

"Andreas Huber" <ah****@gmx.net> wrote in message
news:3e**************************@posting.google.c om...
"Tom Shelton" <ts****@yahoo.com> wrote in message news:<#d**************@TK2MSFTNGP12.phx.gbl>...
"Ron James" <Ro*******@noapsm.rojacs.com> wrote in message
news:09****************************@phx.gbl...

[snip]
Unfortunately this is not so in C# (or Java) since you
cannot tell when the Garbage Collector will run. In C#
you can call Dispose (assuming it's implemented), but you
you still might have several references to a invalid
object floating around. Alternatively you can take
advantage of the using statement IF the class implements
IDispose, however this can become unwieldy if you need
several nested using blocks.


You are not understanding the Dispose pattern. Dispose is only implemented for classes that hold references to unmanaged resources - such as file
handles, window handles, database connections, socket handles, etc.)


Ahem, you don't seem to understand it fully either or I have
misunderstood it completely ;-). Whether a class implements
IDisposable or not has nothing to do with the class having *unmanaged*
resources. When a class implements IDisposable then it provides the
outside world with a means to deterministically clean up resources -
nothing more, nothing less. Some of these resources *might* be
unmanaged of course, but only might. For example, have a look at
System.IO.BinaryWriter. This class implements IDisposable but does
itself not have unmanaged resources. Among other (managed) data
members it owns a System.IO.Stream object and a buffer. The Dispose()
method is only there to flush that buffer and then close the
underlying stream. If you fail to call Dispose() before letting the
BinaryWriter object go out of scope you are guaranteed to lose some
data, because BinaryWriter does not have a finalizer. This is
intentional as you cannot access other managed objects (here: the
stream) in a finalizer. Why? Because the order of finalization of no
longer reachable objects is not defined. You might get lucky sometimes
but it's definitely not guaranteed to work.


Hello! BinaryWritter takes a stream as one of the arguments to both it's
public constructors... That stream is almost certainly going to have an
underlying OS resource - FileStream or NetworkStream for example. In other
words, though BinaryWriter doesn't itself contain a unmanaged resource, it
contains a class that does - hence the need for dispose.
Except for a few *very* esotheric cases, finalizers are only good to
clean up _unmanaged_ resources. E.g. if you have a look at
mscorlib.dll with ILDASM, you'll find that classes which have
unmanaged resources invariably also implement a finalizer. To avoid
code duplication, some of them combine finalization with disposal in a
pattern (the one with the virtual Dispose( bool dispose ) function).
Dispose() can be used to clean up managed *and* unmanaged resources
while Finalize() only releases the unmanaged resources.
What managed resources will you clean up with dispose? Setting internal
references to null isn't going to change anything... The only reason your
example above implements dispose is that is a generic wrapper for a stream.
A stream will almost certainly have a unmanaged resource at the root.
These
are limmited OS resources, that need to be returned as soon as possible. In other words, you don't want to wait around for your finalize method to be called for those resources to be released.


True, but only part of the story, see above.


Except that your story is wrong...
via the IDisposable interface. Generally all necessary clean up is called from both the finalize method as well as the Dispose method, with the added
Finalize() only cleans up unmanaged resources, see above.
Yes, that is what I said, except you have the part about Dispose wrong.
There is no reason to implement IDisposable if there are no unmanaged
resources to clean up. Think about it...
[snip]
2) Referecne counting is easy to screw up. Often they are implmented
using methods that increment or decrement the Ref count and it is up to the
developer or the runtime to make sure these are called in a balanced

manner.
Yep, but it's also quite easy to get right. E.g. see

http://www.boost.org/libs/smart_ptr/shared_ptr.htm


Yes, I am aware of smart pointers...

Tom Shelton
Nov 13 '05 #8

P: n/a

"Andreas Huber" <ah****@gmx.net> wrote in message
news:3f********@news.swissonline.ch...
ncaHammer wrote:
"Andreas Huber" <ah****@gmx.net> wrote in message
news:3e**************************@posting.google.c om...
Finalize() only cleans up unmanaged resources, see above.
QUIZ : Why then Regex has a Finalizer ?
there is a ref counting mechanism under it
this approach is either extremely sophisticated (and i cannot
understand it) or extremely stupid


I've just had a look at the the class with ILDASM. This seems to be one of
the esotheric uses of finalizers I was talking about in the lines above

the one you quoted. Regex has a static HashTable field called "livecode". I'm
far from being proficient with MSIL so I don't really understand the code,
but it seems that all constructors put something in it while the destructor (Finalizer) removes something from it. This is safe because the static data member is supposed (guaranteed?) to exist at least as long as Regex objects.
Definitely, there are other uses for finalizers than just cleaning up
unmanaged resources. However, as I explained, you're quite limited in what
you can safely do with them. The other non-cleanup use I know uses
finalizers to implement an object pool and - no wonder - also only accesses static data members in the finalizer.

I bet you won't find many such finalizers in the .NET framework.

Regards,

Andreas


Having not looked at the IDL for this, my guess form this is that it is
stashing the compiled regular expressions, and then removing them when they
are no longer needed. This I guess a form of cleanup, you have to remove a
reference to the compiled code or it would stay there for ever in the static
object. It is very similar to your reference to object pooling above.

Tom Shelton
Nov 13 '05 #9

P: n/a
"Tom Shelton" <ts****@yahoo.com> wrote in message
news:un**************@TK2MSFTNGP12.phx.gbl...
Yes, that is what I said, except you have the part about Dispose wrong.
There is no reason to implement IDisposable if there are no unmanaged
resources to clean up. Think about it...


All components implement IDisposable and most of them don't hold any
unmanaged resources.
At design time (or at runtime) Disposing a component should remove any
references that any component (under the ISite) holds for it. This action
should restore the ISite component graph to the state it had before adding
this component.

for example :

ToolbarButton while not holding any unmanaged resource, it does implement
IDisposable. Disposing a toolbarbutton it removes it from the button
collection of the toolbar.

I don't know if i make my point clear. (my English are bad)
In a nutshell. Dispose is also used to maintain an object graph consistent.
You can interpret it as "on-delete trigger" in SQL terms
Nov 13 '05 #10

P: n/a

"ncaHammer" <nc*******@nos.pamhot.mail.com> wrote in message
news:OG**************@TK2MSFTNGP11.phx.gbl...
"Tom Shelton" <ts****@yahoo.com> wrote in message
news:un**************@TK2MSFTNGP12.phx.gbl...
Yes, that is what I said, except you have the part about Dispose wrong.
There is no reason to implement IDisposable if there are no unmanaged
resources to clean up. Think about it...


All components implement IDisposable and most of them don't hold any
unmanaged resources.
At design time (or at runtime) Disposing a component should remove any
references that any component (under the ISite) holds for it. This action
should restore the ISite component graph to the state it had before adding
this component.

for example :

ToolbarButton while not holding any unmanaged resource, it does implement
IDisposable. Disposing a toolbarbutton it removes it from the button
collection of the toolbar.


Ok, not something I had thought about - still I'm looking throught IDL :)
This usage seems about as strange as the use of finalize for object pooling.
I can see though why a component would have a dispose method - they just
might have unmanaged resources. What about database components etc...

Tom Shelton
Nov 13 '05 #11

P: n/a

"Tom Shelton" <ts****@yahoo.com> wrote in message
news:%2****************@TK2MSFTNGP12.phx.gbl...

"ncaHammer" <nc*******@nos.pamhot.mail.com> wrote in message
news:OG**************@TK2MSFTNGP11.phx.gbl...
"Tom Shelton" <ts****@yahoo.com> wrote in message
news:un**************@TK2MSFTNGP12.phx.gbl...
Yes, that is what I said, except you have the part about Dispose wrong. There is no reason to implement IDisposable if there are no unmanaged
resources to clean up. Think about it...
All components implement IDisposable and most of them don't hold any
unmanaged resources.
At design time (or at runtime) Disposing a component should remove any
references that any component (under the ISite) holds for it. This action should restore the ISite component graph to the state it had before adding this component.

for example :

ToolbarButton while not holding any unmanaged resource, it does implement IDisposable. Disposing a toolbarbutton it removes it from the button
collection of the toolbar.


Ok, not something I had thought about - still I'm looking throught IDL :)
This usage seems about as strange as the use of finalize for object

pooling. I can see though why a component would have a dispose method - they just
might have unmanaged resources. What about database components etc...

Tom Shelton


Bye the way, I wouldn't say most components do not hold unmanaged
resources... Every winforms control is a component, since every control
inherits from component. I in fact would say that most components, do in
fact hold unmanaged resources (Controls, Timers, Connections, Messaging,
etc)

Tom Shelton
Nov 13 '05 #12

P: n/a
Tom Shelton wrote:
[snip]
Ahem, you don't seem to understand it fully either or I have
misunderstood it completely ;-). Whether a class implements
IDisposable or not has nothing to do with the class having
*unmanaged* resources. When a class implements IDisposable then it
provides the outside world with a means to deterministically clean
up resources - nothing more, nothing less. Some of these resources
*might* be
unmanaged of course, but only might. For example, have a look at
System.IO.BinaryWriter. This class implements IDisposable but does
itself not have unmanaged resources. Among other (managed) data
members it owns a System.IO.Stream object and a buffer. The Dispose()
method is only there to flush that buffer and then close the
underlying stream. If you fail to call Dispose() before letting the
BinaryWriter object go out of scope you are guaranteed to lose some
data, because BinaryWriter does not have a finalizer. This is
intentional as you cannot access other managed objects (here: the
stream) in a finalizer. Why? Because the order of finalization of no
longer reachable objects is not defined. You might get lucky
sometimes but it's definitely not guaranteed to work.
Hello! BinaryWritter takes a stream as one of the arguments to both
it's public constructors... That stream is almost certainly going to
have an underlying OS resource - FileStream or NetworkStream for


Counter example: A MemoryStream object does not hold unmanaged OS resources.
example. In other words, though BinaryWriter doesn't itself contain
a unmanaged resource, it contains a class that does - hence the need
for dispose.
Nope. You need to call Dispose() even if you pass a MemoryStream object to
the BinaryWriter constructor or you are almost guaranteed to lose data,
although there aren't any unmanaged resources anywhere.
Except for a few *very* esotheric cases, finalizers are only good to
clean up _unmanaged_ resources. E.g. if you have a look at
mscorlib.dll with ILDASM, you'll find that classes which have
unmanaged resources invariably also implement a finalizer. To avoid
code duplication, some of them combine finalization with disposal in
a pattern (the one with the virtual Dispose( bool dispose )
function). Dispose() can be used to clean up managed *and* unmanaged
resources
while Finalize() only releases the unmanaged resources.
What managed resources will you clean up with dispose?


As I tried to explain in my previous post, the problem is that BinaryWriter
itself holds a buffer. If you want the contents of that buffer to be flushed
into the stream, you *must* call Dispose(). ~BinaryWriter() (if it existed)
won't do that for you because the underlying stream might already be dead.
Whether or not the stream holds any unmanaged resources is simply
irrelevant.
If you fail to call Dispose() on the BinaryWriter object you will lose data,
even if the stream does not hold unmanaged resources.

[snip] Yes, that is what I said, except you have the part about Dispose
wrong. There is no reason to implement IDisposable if there are no
unmanaged resources to clean up. Think about it...


You are right in as much as that the managed resources will all be released
correctly. However, you are wrong in terms of correctness because the
released resources might hold valuable data, which will simply be thrown
away if you fail to call Dispose() before letting the object go out of
scope.

Regards,

Andreas

Nov 13 '05 #13

P: n/a
Andreas Huber wrote:
[snip]
Nope. You need to call Dispose() even if you pass a MemoryStream
object to the BinaryWriter constructor or you are almost guaranteed
to lose data, although there aren't any unmanaged resources anywhere.

[snip]

Forget about BinaryWriter, it does not have an internal buffer. Instead,
have a look at StreamWriter.

Regards,

Andreas

Nov 13 '05 #14

P: n/a
Hi Ron, gentlmen.

C++ is a great language. Agreed
the stack, grab whatever resources you need, and free them
in the destructor. Object destructors are called in the
correct order (the reverse order), and are guaranteed to
be called when the object goes out of scope.

As far as I can tell, you, as I had once, are missing the good old
"Resource acquisition is initialization (RAII)" idiom. Others have already
answered the technical reasons for choosing garbage collection instead of
reference counting, so I'll just keep my two cents on that subject to myself.
However, may I suggest that you take a look at the following article, which
does attempt to simulate the RAII idiom in C#:
http://www.codeproject.com/csharp/exceptions.asp

Hope you'd find it useful.

Sincerely,
LM.

P.S. My apologies to you and others how are viewing this post if I have broken
proper etiquette, since this is my first post to this group.
Nov 13 '05 #15

This discussion thread is closed

Replies have been disabled for this discussion.