Garbage Collection and unreferenced local variable

G

Guest

Hi all,
I have a question, in the following code:

using System;
using System.Diagnostics;

namespace ConsoleApplication1
{
class TestClass
{
public TestClass()
{
Debug.WriteLine("TestClass constructor");
}

~TestClass()
{
Debug.WriteLine("TestClass finalizer");
}
}

class Program
{
static void Main(string[] args)
{
{
TestClass x = new TestClass();

//Keep compiler honest
Debug.WriteLine(x.ToString());
}

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

TestClass instance x is created in the scope of the {} braces, so after the
closing brace x goes out of scope and it is not possible to reference it any
more. I would think that x should not be considered to be a root for garbage
collection, so when GC.Collect is called the GC would see that nothing
references this instance and call the finalizer on the instance. However
this does not happen, the finalizer is not called, however if I put an
explicit assignment of null to x i.e. "x=null;" inside the {} then it's
finalizer is called.

So my question is, are the {} not considered to dictate garbage collection,
I would hope so, but it does not seem the case, below is the code that does
call the finalizer:

using System;
using System.Diagnostics;

namespace ConsoleApplication1
{
class TestClass
{
public TestClass()
{
Debug.WriteLine("TestClass constructor");
}

~TestClass()
{
Debug.WriteLine("TestClass finalizer");
}
}

class Program
{
static void Main(string[] args)
{
{
TestClass x = new TestClass();

//Keep compiler honest
Debug.WriteLine(x.ToString());

x = null;
}

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

Thanks
dniml.
 
D

DeveloperX

My understanding is that you can't make it finalize anything. You can
invoke the GC, but all that does is ask it to see if it thinks there's
a need to recover used memory. The nearest you can get is to use
IDispose, which still won't clean up, but does give you a sort of
determanistic way to tidy up as the object goes out of scope. It
doesn't happen automatically of course, you have to do it manually, or
use the object in a using block.
 
G

Guest

Hi DeveloperX,
once the object goes out of scope and you forcefully call the GC on all
generations the finalizer should be called (if the object has one) since my
understanding is that the GC will kick off the finalize queue thread, and
the finalizer is deterministically being called in the second code sample
when the variable is explicitly set to null, so I am not sure why I have to
set it to null, when it is already out of scope in terms if the {}.



DeveloperX said:
My understanding is that you can't make it finalize anything. You can
invoke the GC, but all that does is ask it to see if it thinks there's
a need to recover used memory. The nearest you can get is to use
IDispose, which still won't clean up, but does give you a sort of
determanistic way to tidy up as the object goes out of scope. It
doesn't happen automatically of course, you have to do it manually, or
use the object in a using block.

Hi all,
I have a question, in the following code:

using System;
using System.Diagnostics;

namespace ConsoleApplication1
{
class TestClass
{
public TestClass()
{
Debug.WriteLine("TestClass constructor");
}

~TestClass()
{
Debug.WriteLine("TestClass finalizer");
}
}

class Program
{
static void Main(string[] args)
{
{
TestClass x = new TestClass();

//Keep compiler honest
Debug.WriteLine(x.ToString());
}

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

TestClass instance x is created in the scope of the {} braces, so after the
closing brace x goes out of scope and it is not possible to reference it any
more. I would think that x should not be considered to be a root for garbage
collection, so when GC.Collect is called the GC would see that nothing
references this instance and call the finalizer on the instance. However
this does not happen, the finalizer is not called, however if I put an
explicit assignment of null to x i.e. "x=null;" inside the {} then it's
finalizer is called.

So my question is, are the {} not considered to dictate garbage collection,
I would hope so, but it does not seem the case, below is the code that does
call the finalizer:

using System;
using System.Diagnostics;

namespace ConsoleApplication1
{
class TestClass
{
public TestClass()
{
Debug.WriteLine("TestClass constructor");
}

~TestClass()
{
Debug.WriteLine("TestClass finalizer");
}
}

class Program
{
static void Main(string[] args)
{
{
TestClass x = new TestClass();

//Keep compiler honest
Debug.WriteLine(x.ToString());

x = null;
}

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

Thanks
dniml.
 
D

DeveloperX

Yes true, but that's the whole point of the GC, it only does the
reclaimation and thus finalise when it's good and ready, you can't
force it, you can simply ask it if it thinks there's something it needs
to do. The mantra you will hear over and over again is never rely on a
finalizer to do anything.
You're right it will be marked as reclaimable as it goes out of scope
as you leave the inner {} but that doesn't mean the GC will do anything
about it unless it feels like it based on available resources, what
colour socks you're wearing and so on.

Hi DeveloperX,
once the object goes out of scope and you forcefully call the GC on all
generations the finalizer should be called (if the object has one) since my
understanding is that the GC will kick off the finalize queue thread, and
the finalizer is deterministically being called in the second code sample
when the variable is explicitly set to null, so I am not sure why I have to
set it to null, when it is already out of scope in terms if the {}.



DeveloperX said:
My understanding is that you can't make it finalize anything. You can
invoke the GC, but all that does is ask it to see if it thinks there's
a need to recover used memory. The nearest you can get is to use
IDispose, which still won't clean up, but does give you a sort of
determanistic way to tidy up as the object goes out of scope. It
doesn't happen automatically of course, you have to do it manually, or
use the object in a using block.

Hi all,
I have a question, in the following code:

using System;
using System.Diagnostics;

namespace ConsoleApplication1
{
class TestClass
{
public TestClass()
{
Debug.WriteLine("TestClass constructor");
}

~TestClass()
{
Debug.WriteLine("TestClass finalizer");
}
}

class Program
{
static void Main(string[] args)
{
{
TestClass x = new TestClass();

//Keep compiler honest
Debug.WriteLine(x.ToString());
}

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

TestClass instance x is created in the scope of the {} braces, so after the
closing brace x goes out of scope and it is not possible to reference it any
more. I would think that x should not be considered to be a root for garbage
collection, so when GC.Collect is called the GC would see that nothing
references this instance and call the finalizer on the instance. However
this does not happen, the finalizer is not called, however if I put an
explicit assignment of null to x i.e. "x=null;" inside the {} then it's
finalizer is called.

So my question is, are the {} not considered to dictate garbage collection,
I would hope so, but it does not seem the case, below is the code that does
call the finalizer:

using System;
using System.Diagnostics;

namespace ConsoleApplication1
{
class TestClass
{
public TestClass()
{
Debug.WriteLine("TestClass constructor");
}

~TestClass()
{
Debug.WriteLine("TestClass finalizer");
}
}

class Program
{
static void Main(string[] args)
{
{
TestClass x = new TestClass();

//Keep compiler honest
Debug.WriteLine(x.ToString());

x = null;
}

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

Thanks
dniml.
 
K

ktrvnbq02

dotnetismylife said:
[...] so when GC.Collect is called the GC would see that nothing
references this instance and call the finalizer on the instance. However
this does not happen, the finalizer is not called, however if I put an
explicit assignment of null to x i.e. "x=null;" inside the {} then it's
finalizer is called.

So my question is, are the {} not considered to dictate garbage collection,
I would hope so, but it does not seem the case, below is the code that does
call the finalizer:

Is this a Debug or Release build you're testing? They will behave
differently in this regard, IIRC.


Regards,

Matt
 
C

Christof Nordiek

Hi,

I don't think, that the GC will take any notice of the braces. It only looks
if the variable is still used. In a Release build, I guess, the instance
will be finalized on the GC even without the braces. In a debug build it
holds the variable alife for some debugging tool, wich might inspect it. Or
the executionpoint might be set into the braces while a debugging session.
 
D

DeveloperX

Oh I see your point now, I missed the subtle distinction between the
two listings and hadn't notice you didn't set the variable to null in
the first. As you can't do
void method()
{
int x = 0;
{
int x = 1;
}
}



I assume the variable is still in scope until the function exits.
Apologies for the misunderstanding.
 
J

Jon Shemitz

dotnetismylife said:
TestClass instance x is created in the scope of the {} braces, so after the
closing brace x goes out of scope and it is not possible to reference it any
more. I would think that x should not be considered to be a root for garbage
collection, so when GC.Collect is called the GC would see that nothing
references this instance and call the finalizer on the instance. However
this does not happen, the finalizer is not called, however if I put an
explicit assignment of null to x i.e. "x=null;" inside the {} then it's
finalizer is called.

This appears to be an artifact of debug mode and/or running the
release version within VS. When I replace the calls to Debug.WriteLine
with calls to Console.WriteLine, add a WriteLine to let me know when
WaitForPendingFinalizers is called, compile in release mode, and run
the exe from a Windows Explorer window, "TestClass finalizer" does
appear before WaitForPendingFinalizers returns.

For that matter, it works as expected if I comment out the braces
around x.

Bottom line: In normal operation, a variable that is no longer used is
eligible for garbage collection.

//

using System;
using System.Diagnostics;

namespace ConsoleApplication1
{
class TestClass
{
public TestClass()
{
Console.WriteLine("TestClass constructor");
}

~TestClass()
{
Console.WriteLine("TestClass finalizer");
}
}

class Program
{
static void Main(string[] args)
{
//{
TestClass x = new TestClass();

//Keep compiler honest
Console.WriteLine(x.ToString());
//}

GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine("GC.WaitForPendingFinalizers() has
returned");
Console.ReadLine();
}
}
}
 
D

Dustin Campbell

dotnetismylife said:
This appears to be an artifact of debug mode and/or running the
release version within VS. When I replace the calls to Debug.WriteLine
with calls to Console.WriteLine, add a WriteLine to let me know when
WaitForPendingFinalizers is called, compile in release mode, and run
the exe from a Windows Explorer window, "TestClass finalizer" does
appear before WaitForPendingFinalizers returns.

It's actually caused by a little-known option in Visual Studio that disables
JIT optimizations while debugging code that has been optimized (e.g. in Release
mode). To turn it off, follow these steps:

1. Selection "Options..." from the "Tools" menu to display the Options dialog.
2. In the Options dialog, select the "Debugging -> General" page.
3. Scroll all the way to the bottom and uncheck the "Suppress JIT optimization
on module load (Managed only)" box.
4. In addition, uncheck the "Enable Just My Code (Managed only)" box. Otherwise,
VS will complain when you debug the app.
5. Click OK to close the dialog and accept the changes.

Now, if you debug your Release mode app, it'll function properly.

Best Regards,
Dustin Campbell
Developer Express Inc.
 
J

Jon Skeet [C# MVP]

DeveloperX said:
Oh I see your point now, I missed the subtle distinction between the
two listings and hadn't notice you didn't set the variable to null in
the first. As you can't do
void method()
{
int x = 0;
{
int x = 1;
}
}

I assume the variable is still in scope until the function exits.
Apologies for the misunderstanding.

The variable is no longer treated as a GC root as soon as the JIT
notices that it will no longer be read.

For instance:

public void Foo()
{
object o = new object();

for (int i=0; i < 100000; i++)
{
// Do stuff here not related to o
}
}

During the loop, when not running in a debugger, the object created in
the first line is eligible for garbage collection.
 

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