none
GC.keepalive(other) and finalizer of other? RRS feed

  • Question

  • Hi!

    I'm still trying to understand the GC.

    So, if I have something like this:

    class SubFinalizable
    	{
    	public void DoSomething() { }
    	~SubFinalizable()
    		{
    		/// make DoSomething() fail next time it's called.
    		}
    	}
    class SubNormal { }
    class RootFinalizable
    	{
    	SubFinalizable SubF = new SubFinalizable();
    	SubNormal SubN = new SubNormal();
    	~RootFinalizable() { }
    	}
    class Program
    	{
    	static void Main(string[] args)
    		{
    		RootFinalizable R = new RootFinalizable();
    		}
    	}

    can I rely on SubF being usable in ~RootFinalizable()?

    If not, will

    • GC.KeepAlive(SubF) in the Finalizer change this?
    • GC.KeepAlive(this) in the Finalizer make sure all objects referenced will be alive and no Finalizers run called when ~RootFinalizable() runs?

    One of the problems I'm trying to solve is implementing a finalizer for an object encpsulating a database connection. In that case the Finalizer must be able to commit and log off, even if Dispose hasn't been called. So, there's a whole tree of objects (whatever the database connection references internally) that must be in a usable state when my Finalizer starts working.

    Lots of Greetings!


    Volker

    • Edited by Hetzi Saturday, March 24, 2012 12:20 PM
    Saturday, March 24, 2012 12:16 PM

Answers

  • Hi,

     Finalizer must be able to commit and log off

    Call to Finalize method is non-deterministic, when GC determine the object is ready for garbage it checks for any pointer in Finalize queue and if finds remove pointer and pushes to F-reachable queue then a dedicated thread calls to execute, the object may hold on commit and resources until the next time GC gets run and run time doesn't make any guarantees to the order in which Finalize methods are called when there are 2 objects are ready, which have references, for garbage collected.

    We should make sure Finalize method execute as quickly as possible otherwise it would block call to Finalize method and hurts the performance. In your case commit might take bit time.

    As I see when you decide to have Commit in Finalize, you are maintaining transaction in class level. Not advisable transaction keeping for a long.

    You can implement IDisposable and practice using using {} to call Dispose by auto.

    any how For your question, 

    Having GC.KeepAlive(this) at the end of DoSomething will do a trick in your case.

    Some useful links about this

    http://blogs.msdn.com/b/oldnewthing/archive/2010/08/13/10049634.aspx

    http://blogs.msdn.com/b/cbrumme/archive/2003/04/19/51365.aspx

    I hope this helps you...


     


    If this post answers your question, please click "Mark As Answer". If this post is helpful please click "Mark as Helpful".


    • Edited by Kris444 Sunday, March 25, 2012 1:40 PM typo
    • Proposed as answer by Mike FengModerator Monday, March 26, 2012 3:12 AM
    • Marked as answer by Hetzi Monday, March 26, 2012 9:03 AM
    • Unmarked as answer by Hetzi Monday, March 26, 2012 9:23 AM
    • Marked as answer by Hetzi Monday, March 26, 2012 9:23 AM
    Sunday, March 25, 2012 1:37 PM
  • I just tested it, GC.KeepAlive doesn't prevent Finalizers from being run. :-(

    Here's the code:

    class Sub { public int FinalizerHasRun = 0; ~Sub() { FinalizerHasRun = 1; } }
    class Root
    	{
    	public List<Sub> SubsPreCreation = new List<Sub>();
    	public List<Sub> SubsPostCreation = new List<Sub>();
    	~Root()
    		{
    		Console.Out.WriteLine("PreCreationSubs:  "+ String.Join("", SubsPreCreation.Select(i => i.FinalizerHasRun.ToString())));
    		Console.Out.WriteLine("PostCreationSubs: " + String.Join("", SubsPostCreation.Select(i => i.FinalizerHasRun.ToString())));
    		GC.KeepAlive(Console.Out);
    		GC.KeepAlive(SubsPreCreation);
    		GC.KeepAlive(SubsPostCreation);
    		GC.KeepAlive(SubsPostCreation[0]);
    		}
    	}
    class Program
    	{
    	static void Main(string[] args)
    		{
    		List<Sub> Subs = new List<Sub>(50);
    		for (int I = 0; I < 50; I++) { Subs.Add(new Sub()); }
    		Root R = new Root();
    		R.SubsPreCreation.AddRange(Subs);
    		Subs = null;
    		for (int I = 0; I < 50; I++) { R.SubsPostCreation.Add(new Sub()); }
    		GC.KeepAlive(R);
    		}
    	}

    And the output is:

    PreCreationSubs:  00000000000000000000000000000000000000000000000000
    PostCreationSubs: 11111111111111111111111111111111111111111111111111

    Bad luck. This does limit the usefulness of finalizers quite a lot!


    Volker

    • Marked as answer by Hetzi Monday, March 26, 2012 9:23 AM
    Monday, March 26, 2012 9:23 AM

All replies

  • Is you database connection a managed object? If so, you don't need to worry about it in your finalizer. If your object is in the finalizer queue, whatever it's referencing to is still in memory and hasn't been collected yet.

    Saturday, March 24, 2012 11:45 PM
  • Hi Hetzi,

    My feeling is, whenever RootFinalizable destructor is called, it will handle SubF. But if SubF is handling any database connections, you have to go and handle it manually.


    Regards, http://shwetamannjain.blogspot.com

    Sunday, March 25, 2012 7:04 AM
  • Ok, a possibly better example:

    class SubFinalizable
    	{
    	public void DoSomething() { }
    	~SubFinalizable()
    		{
    		/// make DoSomething() fail next time it's called.
    		}
    	}
    class SubNormal { }
    class RootFinalizable
    	{
    	SubFinalizable SubF = new SubFinalizable();
    	SubNormal SubN = new SubNormal();
    	~RootFinalizable()
    		{
    		SubF.DoSomething();
    		}
    	}

    The problem is that, yes, SubF's memory is not collected but according to this, the order the finalizers are run is not guaranteed.

    So, when I call SubF.DoSomething(), SubF's finalizer might already have been executed and DoSomething will not work anymore.

    I'm trying ot figure out if GC.KeepAlive(SubF) changes that and delays SubF's finalization too.

    lots of Greetings!


    Volker

    Sunday, March 25, 2012 8:28 AM
  • Hi Hetzi,

    My feeling is, whenever RootFinalizable destructor is called, it will handle SubF. But if SubF is handling any database connections, you have to go and handle it manually.

    Yes, SubF is handling the database connection. However, in that scenario it seems to be possible for R's Finalizer to be called after SubF's finalizer has already finished, making it impossible to do that.

    Volker

    Sunday, March 25, 2012 8:30 AM
  • Hi,

     Finalizer must be able to commit and log off

    Call to Finalize method is non-deterministic, when GC determine the object is ready for garbage it checks for any pointer in Finalize queue and if finds remove pointer and pushes to F-reachable queue then a dedicated thread calls to execute, the object may hold on commit and resources until the next time GC gets run and run time doesn't make any guarantees to the order in which Finalize methods are called when there are 2 objects are ready, which have references, for garbage collected.

    We should make sure Finalize method execute as quickly as possible otherwise it would block call to Finalize method and hurts the performance. In your case commit might take bit time.

    As I see when you decide to have Commit in Finalize, you are maintaining transaction in class level. Not advisable transaction keeping for a long.

    You can implement IDisposable and practice using using {} to call Dispose by auto.

    any how For your question, 

    Having GC.KeepAlive(this) at the end of DoSomething will do a trick in your case.

    Some useful links about this

    http://blogs.msdn.com/b/oldnewthing/archive/2010/08/13/10049634.aspx

    http://blogs.msdn.com/b/cbrumme/archive/2003/04/19/51365.aspx

    I hope this helps you...


     


    If this post answers your question, please click "Mark As Answer". If this post is helpful please click "Mark as Helpful".


    • Edited by Kris444 Sunday, March 25, 2012 1:40 PM typo
    • Proposed as answer by Mike FengModerator Monday, March 26, 2012 3:12 AM
    • Marked as answer by Hetzi Monday, March 26, 2012 9:03 AM
    • Unmarked as answer by Hetzi Monday, March 26, 2012 9:23 AM
    • Marked as answer by Hetzi Monday, March 26, 2012 9:23 AM
    Sunday, March 25, 2012 1:37 PM
  • Since SubF has a finalizer RootFinalizable's finalizer isn't to worry about it at all. Finalizing that object is the responsibility of the finalizer thread, not any of your objects.
    Sunday, March 25, 2012 3:20 PM
  • Hi,

     Finalizer must be able to commit and log off

    Call to Finalize method is non-deterministic, when GC determine the object is ready for garbage it checks for any pointer in Finalize queue and if finds remove pointer and pushes to F-reachable queue then a dedicated thread calls to execute, the object may hold on commit and resources until the next time GC gets run and run time doesn't make any guarantees to the order in which Finalize methods are called when there are 2 objects are ready, which have references, for garbage collected.

    [...]

    Some useful links about this

    http://blogs.msdn.com/b/oldnewthing/archive/2010/08/13/10049634.asp

    The link appears to say that the finalizer of SubF will not be executed, if I have a GC.KeepAlive(SubF) in my Rootfinalizable Finalizer.

    I'll see how I can test that.

    Thanks a lot!


    Volker

    Monday, March 26, 2012 9:03 AM
  • In theory yes. But my case is that I want to do more than SubFinalizable's Finalizer.


    Volker

    Monday, March 26, 2012 9:06 AM
  • I just tested it, GC.KeepAlive doesn't prevent Finalizers from being run. :-(

    Here's the code:

    class Sub { public int FinalizerHasRun = 0; ~Sub() { FinalizerHasRun = 1; } }
    class Root
    	{
    	public List<Sub> SubsPreCreation = new List<Sub>();
    	public List<Sub> SubsPostCreation = new List<Sub>();
    	~Root()
    		{
    		Console.Out.WriteLine("PreCreationSubs:  "+ String.Join("", SubsPreCreation.Select(i => i.FinalizerHasRun.ToString())));
    		Console.Out.WriteLine("PostCreationSubs: " + String.Join("", SubsPostCreation.Select(i => i.FinalizerHasRun.ToString())));
    		GC.KeepAlive(Console.Out);
    		GC.KeepAlive(SubsPreCreation);
    		GC.KeepAlive(SubsPostCreation);
    		GC.KeepAlive(SubsPostCreation[0]);
    		}
    	}
    class Program
    	{
    	static void Main(string[] args)
    		{
    		List<Sub> Subs = new List<Sub>(50);
    		for (int I = 0; I < 50; I++) { Subs.Add(new Sub()); }
    		Root R = new Root();
    		R.SubsPreCreation.AddRange(Subs);
    		Subs = null;
    		for (int I = 0; I < 50; I++) { R.SubsPostCreation.Add(new Sub()); }
    		GC.KeepAlive(R);
    		}
    	}

    And the output is:

    PreCreationSubs:  00000000000000000000000000000000000000000000000000
    PostCreationSubs: 11111111111111111111111111111111111111111111111111

    Bad luck. This does limit the usefulness of finalizers quite a lot!


    Volker

    • Marked as answer by Hetzi Monday, March 26, 2012 9:23 AM
    Monday, March 26, 2012 9:23 AM
  • Hetzi,

    You're trying to use finalizer's like disposable, which will never work. Diposable's are for intentional cleaning up, and finalizers are a defense-in-depth measure to have a second chance, should someone have not disposed properly. If an application is programed properly a finalize method will never run, because the dispose methods take care of everything. 

    Monday, March 26, 2012 4:25 PM
  • Hetzi,

    You're trying to use finalizer's like disposable, which will never work. Diposable's are for intentional cleaning up, and finalizers are a defense-in-depth measure to have a second chance, should someone have not disposed properly. If an application is programed properly a finalize method will never run, because the dispose methods take care of everything. 

    I fully agree that Dispose ought to be used and I only left it off for demonstration purposes. But:

    • There are cases where this is extremely impractical: IEnumerator<...>.Where(i=>...).Select(...).Sum() . Now what? Does the Enumerator keep a copy of the sequence? Can it even do that? Is it desirable to draw out the whole sequence into a list and then start to work on the elements?
    • A second chance means to me that I can do in a finalizer what I can do in Dispose(). Unfortunately that's not possible.

    In my opinion the finalizer queue should be a real root, that is, no object that gets referred to from an object on the queue is supposed to be on any waiting list for the GC. Only when the object is finalized and collected, should the others become eligible for  finalization and collection.


    Volker

    Monday, March 26, 2012 5:50 PM
  • GC.KeepAlive() is syntactic sugar. It doesn't actually do anything. In code like this:

    Thing mything = new Thing();

    .............

    ............

    GC.KeepAlive(mything);

    all that's happening is that there is now a reference to mything later in the code, without which it would have been a candidate for garbage collection. It just adds a reference to an object that you don't want to be collected for some reason. If it's being passed out into unmanaged code and .NET can't manage its references, it may be collected while still in use by that code. KeepAlive prevents that. That's the general idea anyway, and is what the documentation says. It does not literally keep the object alive - references do that.


    Phil Wilson

    Tuesday, March 27, 2012 6:17 PM
  • Ok, i think I'll have to live with this.

    Thanks everybody!


    Volker

    Wednesday, March 28, 2012 10:39 AM