Are the child objects still alive when Object.Finalize is called by the GC?

Let's say I have this class:

class Test
{
    readonly object _child = new Object();

    // ...

    ~Test()
    {
        // access _child here
        // ...
    }
}

      

Is an object _child

guaranteed to be alive when ~Test

called by the garbage collector? Or should I "bind" _child

to GCHandle.Alloc

in the constructor first?

+2


source to share


3 answers


As a field readonly

_child

, you cannot lose your reference (unless you set it to null by reflection). This means that until Test

garbage is collected, it _child

will remain in memory for sure.

Also, you are using Finalizer

which is called before garbage collection. Only on the next pass will the object's memory be restored, at the moment it _child

will be alive. In other words, when a method Finalize

is called _child

, it will be available and safe to access.

The Finalizer is called does not mean that the memory will be reclaimed. If you do something like the following Finalize

, it will be called, but the memory will not be fixed

class Test
{
    readonly object _child = new Object();

    private static Test evilInstance;

    ~Test()
    {
        evilInstance = this;//Do something crazy
        //This resurrects this instance, so memory will not be reclaimed.
    }
}

      

Finalizers are almost never needed when you are dealing with managed code. It adds extra work to the garbage collector, and strange things can happen as we saw above.



Update . If you're _child

only using for lock

, it's safe to use because the instance _child

won't be empty, which means it points to a valid link. Monitor.Enter

and Monitor.Exit

just take care of links that are completely safe to use (block only).

What if you want the child finalizer to be called only after the Test's

finalizer is called?

There is a workaround: you can inherit a class Child

from SafeHandle

and that does the trick. It will make sure both Test

and Child

go out of scope at the same time, it will call the Test's

finalizer first since it Child

inherits from SafeHandle

, which delays its completion. But IMO is not dependent on this. Because other programmers working with you may not know this, which leads to confusion.

This critical finalizer also has a weak order guarantee, stating that if the normal end object and the critical end object become unavailable at the same time, then the normal object finalizer is fired first.

Quote from SafeHandle: An example of using reliability

+2


source


The most pertinent explanation so far IMHO can be found here (see Carlsen's answer), and so to summarize the OP's answer:

Any accessible (child or external) object during object finalization will still be available in memory, but the state of such an object will depend on whether that object has already been finalized by itself, since there is no specific order of completion between objects in the finalization queue.

In short, quoting the post:



You cannot access the objects referenced by your object with finalizers, since you have no guarantee that those objects will be in a healthy state when your finalizer is running. Objects will still be present in memory and not collected, but they can be closed, completed, completed, etc. Already.

However, you can safely use any other accessible object (without finalization) during the object completion stage, for example. line. In practice, this means that it is safe to use any accessible object that does NOT implement the IDisposable interface.

+1


source


I created a simple test:

using System;

namespace ConsoleApplication1
{
    class Program

    {
        class Child
        {
            public override string ToString()
            {
                return "I am a child!";
            }

            ~Child()
            {
                Console.WriteLine("Child finalized");
            }
        }

        class Test
        {
            readonly object _child = new Child();
            readonly WeakReference _ref;

            public Test()
            {
                _ref = new WeakReference(_child);
            }

            // ...

            ~Test()
            {
                Console.WriteLine("Test finalized, child: " 
                    + _child.ToString() + ", is alive: " 
                    + _ref.IsAlive);
            }
        }

        static void Main(string[] args)
        {
            var test = new Test();
            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
            Console.ReadLine();
        }
    }
}

      

Release build:

Child finalized
Test finalized, child: I am a child !, is alive: False

So it _child

ends up first. So, I need to be done GCHandle.Alloc(_child)

in the constructor and done GCHandle.Free

in the finalizer after I'm done with _child

.

To answer the comment, why do I need all this: _child

refers to a method that is called from the finalizer to access an unmanaged resource. This method does lock (_child) { ... }

, and can also be called from outside the finalizer.

0


source







All Articles