GC.Collect doesn't trigger finalizers?

G

Guest

Hi all,

I am building a VB.Net app on Framework 1.1 using VS.Net 2003. I have a
special logging *singleton* class that needs to do work once every object in
my application has either finalized or been disposed of.

The way I have been doing it is by

1) removing my logging class from the GC's finalizer list on creation:
Public Sub New()
GC.SuppressFinalize(Me)
End Sub

2) handling the AppDomain.CurrentDomain.ProcessExit event in my logging
class :
Private Sub AtProcessExitEventHandler(ByVal sender As Object, ByVal e As
EventArgs)
GC.Collect()
GC.WaitForPendingFinalizers()
System.Threading.Thread.Sleep(1000)
Call AnalyseResults()
End Sub

My problem is that none of my (non-IDisposable) application object
finalizers ever trigger before my logging class has finished doing its work.

The call order goes like this: (validated using breakpoints)

A) My Main method finishes up (all my dispose methods have been called at
that point)
B) The ProcessExit events triggers
C) AtProcessExitEventHandler is executed completely (GC.Collect,
WaitForPendingFinalizers, Thread Sleep and AnalyseResults)
D) THEN, the finalize methods of all finalizable objects are called. (???)

I need D) to come before C). How can I force the Finalizers to be called in
that context?

Tough one heh?

Thanks,

-Etienne Thouin
..Net Technical Lead
AlphaCode R&D
 
G

Guest

It might have something to do with the way Garbage Collection works. When you
create an object with a Finalize method a pointer gets put into a
Finalization Queue. With .NET you don't want to rely on this method because
you can't guarantee when it will be called. A Finalize method actually delays
full collection of that object. When garbage collection is invoked objects
with that Finalize method get put into the Freachable queue. Once this
happens memory is compacted and a separate thread is responsible for callling
the Finalize method on that queue. In essence, the object is killed and then
"resurrected" by the thread responsible for the Freachable queue to perform
that final method. I would not recommend relying upon the Finalize method
because of the uncertainty of the timeframe in which it will be called.
 
G

Guest

Well, I still need to know within the main thread when the finalizers are
done (needed by an important feature of my app, no way arround it, really).

You would expect that the WaitForPendingFinalizers method would do that but
it seems that I cannot rely on the WaitForPendingFinalizers method for the
finalizers to execute. Therefore is there a way to get to the garbage
collecting thread diretly to know when the finalizers are done? Or am I
using WaitForPendingFinalizers incorrectly?

Trying to be creative, I could implement IDisposable methods everywhere
(even on object that have no unmanaged resources) so that I could
programmatically force the disposing of every objects, but would that be
mightily ugly.
 
G

Guest

Ok, so you have to know when everything is dead essentially. I think you are
going to have to take a programmitic approach to the disposal. The only other
thing I can think of is to create a callback to the logging class in the
finalize method of each object so it notifies your singleton object that it
has been finalized. It's hard to say if this would work due to the state of
the object without any experimentation. Have you. You would still then have
to keep track of all of your objects to make sure you got notification that
all had been finalized. Tough situation. Well, I just thought of something
else. Could it be a weak reference somewhere that is keeping it alive until
after your logging class is done?
 
R

Richard Blewett [DevelopMentor]

The object doesn't get killed at all. The GC simply sees that it has no rooted reference and as it has it in the finalization queue moves it to the freachable queue. As a result the object will be promoted to the next generation (assuming it is not in Gen2 already).

Regards

Richard Blewett - DevelopMentor
http://www.dotnetconsult.co.uk/weblog
http://www.dotnetconsult.co.uk

It might have something to do with the way Garbage Collection works. When you
create an object with a Finalize method a pointer gets put into a
Finalization Queue. With .NET you don't want to rely on this method because
you can't guarantee when it will be called. A Finalize method actually delays
full collection of that object. When garbage collection is invoked objects
with that Finalize method get put into the Freachable queue. Once this
happens memory is compacted and a separate thread is responsible for callling
the Finalize method on that queue. In essence, the object is killed and then
"resurrected" by the thread responsible for the Freachable queue to perform
that final method. I would not recommend relying upon the Finalize method
because of the uncertainty of the timeframe in which it will be called.
 
G

Guest

Bad wording on my part. You are correct. I used the word killed because it
should be dead to you as a developer. You shouldn't be trying to access it or
do anything to it when it is at that point.
 
G

Guest

The more I think about this, the more it seems like you have a reference in
your logging class that is keeping your other objects from being finalized.
The following I pulled from an article on C# Corner:

When an application terminates, some objects are still reachable and will
not have their Finalize method called. This can happen if background threads
are using the objects or if objects are created during application shutdown
or AppDomain unloading. In addition, by default, Finalize methods are not
called for unreachable objects when an application exits so that the
application may terminate quickly. Of course, all operating system resources
will be reclaimed, but any objects in the managed heap are not able to clean
up gracefully. You can change this default behavior by calling the System.GC
type's RequestFinalizeOnShutdown method. However, you should use this method
with care since calling it means that your type is controlling a policy for
the entire application.

http://www.c-sharpcorner.com/Code/2003/Nov/MemoryManagementInNet.asp

Just a thought.
 
R

Richard Blewett [DevelopMentor]

Why are you finding WaitForPendingFinalizers not working?

The finalizers won't be "pending" unless the GC has noticed that these objects are garbage. Whether a GC thinks objects are garbage depends sometimes on whether you are running a debug or release build. For example:

using System;

class App
{
static void Main()
{
object o1 = new object();
object o2 = new object();
GC.Collect();
o2.ToString();
}
}

on a debug build neother of the objects will be collected, on a release build the object that o1 refers to will be collected as it is no longer used.

Why are you using a finalizer? What are you trying to free with it?

Regards

Richard Blewett - DevelopMentor
http://www.dotnetconsult.co.uk/weblog
http://www.dotnetconsult.co.uk

Well, I still need to know within the main thread when the finalizers are
done (needed by an important feature of my app, no way arround it, really).

You would expect that the WaitForPendingFinalizers method would do that but
it seems that I cannot rely on the WaitForPendingFinalizers method for the
finalizers to execute. Therefore is there a way to get to the garbage
collecting thread diretly to know when the finalizers are done? Or am I
using WaitForPendingFinalizers incorrectly?

Trying to be creative, I could implement IDisposable methods everywhere
(even on object that have no unmanaged resources) so that I could
programmatically force the disposing of every objects, but would that be
mightily ugly.
 
G

Guest

Nice ideas guys, I will start experimenting tonight.

I have tried deliberately to keep my logger pointer-free in the sense that
it doesn't maintain references to foreign object instances. Other objects may
hold a pointer to it thought. RequestFinalizeOnShutdown is a new one for me,
I'll give it a try...

Also, I didn't try to compile in release mode. I'll try that one out. I've
been scratching my head as to why the WaitForFinalize method doesn't work
properly...

Keep you posted.
 
G

Guest

I've been experimenting a bit and here are the results:

GC.Collect simply doesn't seem to work at the ProcessExit event. The system
probably considers that it is already in a state of garbage collecting, or
that since the application is not running anymore, it may not need accepting
GC.Collect calls... Anyhow, if I call my logger at the last line of the
application's Main method (while the application is still running), and that
the logger itself calls GC.Collect and WaitForFinalizers once before doing
its work, the events fire in the correct order and everything works perfectly.

It would have been nice thought if the logger(singleton) could have
triggered automatically at application exit without the need to call it
explicitely...

Anyways, that's that.

Thanks for the help,

E.T.
 
W

Willy Denoyette [MVP]

This is by design, when the program's main thread reaches an exit point
(Environment.Exit or Main method exit) an orderly shutdown is started by the
CLR and the Finalizer thread is activated to finalize all unreachable
objects. When this is done (or timed out) the Finalizer thread is instructed
to participate in the shutdown and finish it's normal activity, at this
point the ProcessExit event is fired and the CLR won't any longer start any
managed activity (GC.Collect, object creation etc...).
That means that the GC will no longer be activated and the Finalizer thread
will finalize the "reachable" objects. At this point the system is no longer
behaving normally, it's no longer possible to depend on objects like Events,
WaitHandles, remoting infrastructure etc..
So if you want your singleton Finalize to run before this event, you have to
release the reference and call GC.Collect before exiting Main or before
calling Environment.Exit.

Willy.
 

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