471,350 Members | 1,723 Online
Bytes | Software Development & Data Engineering Community
Post +

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 471,350 software developers and data experts.

=?Utf-8?Q?garbage_collect=E2=80=94=E2=80=94is_a_bug=EF=B C=9F?=

static tc gto;

public class tc
{
public int a=99;

~tc()
{
a=-1; //set breakpoint 1
gto=this;
}
}

private void button1_Click(object sender,EventArgs e)
{
tc to=new tc();
GC.Collect();
GC.WaitForPendingFinalizers();
return; // set breakpoint 2
}

two problems:
click button once,it seem the ~tc() not execute at all.and it will excute at
the second time clicking.why?
the second clicking,breakpoint 2, "gto" and "to", gto.a=-1,while to.a=99,why?
Aug 31 '08 #1
12 1489
On Sat, 30 Aug 2008 17:34:00 -0700, chinasprit
<ch********@discussions.microsoft.comwrote:
[...]
two problems:
click button once,it seem the ~tc() not execute at all.and it will
excute at
the second time clicking.why?
Are you using a debug build or an optimized build? Have you tried a
similar test without a debugger (which could affect reachability of
objects)?

It's true that the object referenced by "to" is eligible for collection
when you call GC.Collect(). But I'm not aware of any _requirement_ that
it be collected. And when debugging, or for a debug build, it very well
might not be.
the second clicking,breakpoint 2, "gto" and "to", gto.a=-1,while
to.a=99,why?
Why not? The object referenced by "gto" has had its "a" member set to -1,
which is why you get that value. The obejct referenced by "to" has not,
which is why you get 99 instead. Since your first click of the button
already demonstrated that the object referenced by the local variable
isn't being collected, you shouldn't be surprised that it's also not
collected the second time you click the button.

Please see attached below a console application that is essentially the
same test you're doing, but as a true, useful, concise-but-complete code
example. When I run it under the debugger, the object is never finalized
until after the called method returns. When I compile it as a "Debug"
build, that behavior also occurs even when run standalone, without the
debugger.

But when I compile it as a "Release" build and run it standalone, the
object is released, just as it's _allowed_ to be (note difference between
"required" and "allowed").

Now, all that said...there's practically no situation in which you should
_care_ if or when an object is finalized or collected. So, even if this
were a bug in the GC (and it's not), it's not something that should matter
to your own code. If you've got code where it does matter, you've got a
basic design flaw that you should fix.

Pete
using System;

namespace TestUnreachableLocalGC
{
class Program
{
class A
{
static A _a;

int i = 1;

~A()
{
i = -1;
if (_a == null)
{
_a = this;
}
}
}

static void Test()
{
A a = new A();
WeakReference wrA = new WeakReference(a);

GC.Collect();
GC.WaitForPendingFinalizers();

Console.WriteLine("object still valid: " + wrA.IsAlive);
}

static void Main(string[] args)
{
Test();
Console.ReadLine();
Test();
Console.ReadLine();
}
}
}
Aug 31 '08 #2
"chinasprit" <ch********@discussions.microsoft.comwrote in message
news:20**********************************@microsof t.com...
static tc gto;

public class tc
{
public int a=99;

~tc()
{
a=-1; //set breakpoint 1
gto=this;
}
}

private void button1_Click(object sender,EventArgs e)
{
tc to=new tc();
GC.Collect();
GC.WaitForPendingFinalizers();
return; // set breakpoint 2
}

two problems:
click button once,it seem the ~tc() not execute at all.and it will excute
at
the second time clicking.why?
I remember reading something about the GC disposing objects on the first run
(if they have IDispose) and destroying them on the second run. If that is
the case then your second point makes sense because you're looking at 2
different objects and the second object has not been disposed yet.
the second clicking,breakpoint 2, "gto" and "to", gto.a=-1,while
to.a=99,why?


Aug 31 '08 #3
Michael C <mi**@nospam.comwrote:
I remember reading something about the GC disposing objects on the first run
(if they have IDispose) and destroying them on the second run. If that is
the case then your second point makes sense because you're looking at 2
different objects and the second object has not been disposed yet.
The garbage collector itself doesn't know about IDisposable at all. It
calls finalizers, which may in turn call Dispose, but it doesn't call
Dispose directly itself.

--
Jon Skeet - <sk***@pobox.com>
Web site: http://www.pobox.com/~skeet
Blog: http://www.msmvps.com/jon.skeet
C# in Depth: http://csharpindepth.com
Aug 31 '08 #4
chinasprit <ch********@discussions.microsoft.comwrote:
static tc gto;

public class tc
{
public int a=99;

~tc()
{
a=-1; //set breakpoint 1
gto=this;
}
}

private void button1_Click(object sender,EventArgs e)
{
tc to=new tc();
GC.Collect();
GC.WaitForPendingFinalizers();
return; // set breakpoint 2
}

two problems:
click button once,it seem the ~tc() not execute at all.and it will excute at
the second time clicking.why?
the second clicking,breakpoint 2, "gto" and "to", gto.a=-1,while to.a=99,why?
When you run in a debugger, local variables prevent garbage collection
until the end of the message (or at least the scope; I can't remember
off hand). Change from using breakpoints to some other diagnostic (e.g.
a message box) and run without debugging - you'll get the behaviour you
expect.

Alternatively, put "to = null;" before the call to GC.Collect().

--
Jon Skeet - <sk***@pobox.com>
Web site: http://www.pobox.com/~skeet
Blog: http://www.msmvps.com/jon.skeet
C# in Depth: http://csharpindepth.com
Aug 31 '08 #5
On Sat, 30 Aug 2008 23:33:24 -0700, Jon Skeet [C# MVP] <sk***@pobox.com>
wrote:
When you run in a debugger, local variables prevent garbage collection
until the end of the message (or at least the scope; I can't remember
off hand). Change from using breakpoints to some other diagnostic (e.g.
a message box) and run without debugging - you'll get the behaviour you
expect.
In a "Release" build. At least in my test, the "Debug" build didn't
bother to collect the object until after the method exited. The compiled
IL was basically the same, but obviously it's treated differently. Either
the JIT compiler generates different output, or the garbage collector
itself handles the decision-making differently for "Debug" and "Release"
builds (I didn't bother to investigate any further than that).

Pete
Aug 31 '08 #6
Peter Duniho <Np*********@nnowslpianmk.comwrote:
On Sat, 30 Aug 2008 23:33:24 -0700, Jon Skeet [C# MVP] <sk***@pobox.com>
wrote:
When you run in a debugger, local variables prevent garbage collection
until the end of the message (or at least the scope; I can't remember
off hand). Change from using breakpoints to some other diagnostic (e.g.
a message box) and run without debugging - you'll get the behaviour you
expect.

In a "Release" build. At least in my test, the "Debug" build didn't
bother to collect the object until after the method exited. The compiled
IL was basically the same, but obviously it's treated differently. Either
the JIT compiler generates different output, or the garbage collector
itself handles the decision-making differently for "Debug" and "Release"
builds (I didn't bother to investigate any further than that).
Slightly more information - I think it's the optimisation side of
things, not the debug information. Here's a very short program to
demonstrate it:

using System;

class Test
{
~Test()
{
Console.WriteLine("Finalized");
}

static void Main()
{
Test t = new Test();
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine("Hit return...");
Console.ReadLine();
}
}

Compiling with "/debug+ /o-" I don't see the finalization message until
after I've hit return. Compiling with "/debug+ /o+" I see it
immediately.

The IL is significantly different for the optimised version - it
doesn't store the value in the local variable at all, just pops the
constructed value from the stack immediately.

--
Jon Skeet - <sk***@pobox.com>
Web site: http://www.pobox.com/~skeet
Blog: http://www.msmvps.com/jon.skeet
C# in Depth: http://csharpindepth.com
Aug 31 '08 #7
On Sun, 31 Aug 2008 01:03:19 -0700, Jon Skeet [C# MVP] <sk***@pobox.com>
wrote:
[...]
Slightly more information - I think it's the optimisation side of
things, not the debug information.
When I have more time, I'll look again. But for now, I'm going with "c,
both of the above". That is...
[...]
The IL is significantly different for the optimised version - it
doesn't store the value in the local variable at all, just pops the
constructed value from the stack immediately.
The IL I looked at definitely wasn't significantly different for the
"Release" build. In fact, other than not having the "nop" instructions
the "Debug" build had, it was basically the same.

My current theory: you can explicitly set optimization on the C# compiler
settings that does make this happen. But the JIT compiler _also_ does
optimizations that can cause this to happen, even though the default
optimization settings that VS is using (for me, anyway) for the "Release"
build wouldn't otherwise.

Of course, it begs an entirely unrelated question: who does optimization
better? The C# compiler? The JIT compiler? Or some combination?

Pete
Aug 31 '08 #8
Peter Duniho <Np*********@nnowslpianmk.comwrote:

<snip>
Of course, it begs an entirely unrelated question: who does optimization
better? The C# compiler? The JIT compiler? Or some combination?
The C# compiler doesn't do very much of it, which makes sense IMO -
having a good JIT optimiser benefits *all* languages. Obviously there's
a cost at execution time, of course - and a bit more tweakability would
be welcome there (e.g. as an app.config option to say "feel free to
take a long time to optimise the heck out of this code").

--
Jon Skeet - <sk***@pobox.com>
Web site: http://www.pobox.com/~skeet
Blog: http://www.msmvps.com/jon.skeet
C# in Depth: http://csharpindepth.com
Aug 31 '08 #9
Michael C wrote:
"chinasprit" <ch********@discussions.microsoft.comwrote in message
news:20**********************************@microsof t.com...
>static tc gto;

public class tc
{
public int a=99;

~tc()
{
a=-1; //set breakpoint 1
gto=this;
}
}

private void button1_Click(object sender,EventArgs e)
{
tc to=new tc();
GC.Collect();
GC.WaitForPendingFinalizers();
return; // set breakpoint 2
}

two problems:
click button once,it seem the ~tc() not execute at all.and it will excute
at
the second time clicking.why?

I remember reading something about the GC disposing objects on the first run
(if they have IDispose) and destroying them on the second run.
Not exactly.

If an object that is unreachable has a finalizer, it will be placed on
the freachable queue, where a background thread will execute the
finalizer. After the finalizer has been run, the object can be collected
just like any other object.
If that is
the case then your second point makes sense because you're looking at 2
different objects and the second object has not been disposed yet.
>the second clicking,breakpoint 2, "gto" and "to", gto.a=-1,while
to.a=99,why?



--
Göran Andersson
_____
http://www.guffa.com
Aug 31 '08 #10
chinasprit wrote:
static tc gto;

public class tc
{
public int a=99;

~tc()
{
a=-1; //set breakpoint 1
gto=this;
}
}

private void button1_Click(object sender,EventArgs e)
{
tc to=new tc();
GC.Collect();
GC.WaitForPendingFinalizers();
return; // set breakpoint 2
}

two problems:
click button once,it seem the ~tc() not execute at all.and it will excute at
the second time clicking.why?
Calling GC.Collect doesn't guarantee that a garbage collection is
actually performed. If the garbage collector feels that there is too
little to be done, it can just skip the collection.

Also, as you are running in debug mode, the garbage collector looks at
the usage of variables quite differently. In release mode, the object
that you create immediately becomes unreachable, as the variable "to" is
never used any more. In debug mode, the "to" variable is considered in
use it's entire scope, i.e. as long as the debugger can display it's
value, so the object can't be collected until after exiting the method.
the second clicking,breakpoint 2, "gto" and "to", gto.a=-1,while to.a=99,why?
As the newly created object can't be collected until the method exits,
to.a can never be anything but 99 in debug mode.

--
Göran Andersson
_____
http://www.guffa.com
Aug 31 '08 #11
"Jon Skeet [C# MVP]" <sk***@pobox.comwrote in message
news:MP*********************@msnews.microsoft.com. ..
chinasprit <ch********@discussions.microsoft.comwrote:
>static tc gto;

public class tc
{
public int a=99;

~tc()
{
a=-1; //set breakpoint 1
gto=this;
}
}

private void button1_Click(object sender,EventArgs e)
{
tc to=new tc();
GC.Collect();
GC.WaitForPendingFinalizers();
return; // set breakpoint 2
}

two problems:
click button once,it seem the ~tc() not execute at all.and it will excute
at
the second time clicking.why?
the second clicking,breakpoint 2, "gto" and "to", gto.a=-1,while
to.a=99,why?

When you run in a debugger, local variables prevent garbage collection
until the end of the message (or at least the scope; I can't remember
off hand). Change from using breakpoints to some other diagnostic (e.g.
a message box) and run without debugging - you'll get the behaviour you
expect.

Alternatively, put "to = null;" before the call to GC.Collect().

--
Jon Skeet - <sk***@pobox.com>
Web site: http://www.pobox.com/~skeet
Blog: http://www.msmvps.com/jon.skeet
C# in Depth: http://csharpindepth.com

From what I've seen, the debugger holds local variables around until they go
out of scope. This is why you can review a local variable's value even
after the last reference to it in your code. This is a functional
requirement of any descent debugger. Since the variable is still accessible
through the debugger, it can't be garbage collected. The bug would have
been the GC reclaiming it before the end of the visible scope.

In release code, the compile should be able to determine using lexical
analysis when to insert a "to = null" for you so that the GC can reclaim the
memory.

Mike Ober.
Aug 31 '08 #12
Michael D. Ober wrote:
In release code, the compile should be able to determine using lexical
analysis when to insert a "to = null" for you so that the GC can reclaim
the memory.
Actually, there is no code inserted to clear the reference, as that is
not needed. The garbage collector knows where the variables are used,
and can determine that the object is unreachable eventhough the variable
still contains a reference to it.

--
Göran Andersson
_____
http://www.guffa.com
Aug 31 '08 #13

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

4 posts views Thread by sinasalek | last post: by
5 posts views Thread by Richard Lewis | last post: by
6 posts views Thread by Spamtrap | last post: by
1 post views Thread by ryang | last post: by
reply views Thread by Sagi Bashari | last post: by
reply views Thread by XIAOLAOHU | last post: by

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.