Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Say, I have this class:

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

    // ...

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

Is the _child object guaranteed to still be alive when ~Test is called by the garbage collector? Or should I first "pin" the _child with GCHandle.Alloc in the constructor?

like image 747
avo Avatar asked Sep 05 '25 03:09

avo


1 Answers

Being a readonly field _child you can't lose its reference (unless you set it to null via reflection). Which means that till the Test is garbage collected _child will stay in memory for sure.

Also, you're using a Finalizer which is called prior to garbage collection, Only on next pass the object's memory will be reclaimed, at this point _child will be alive. In other words when Finalize method is called _child will be alive and safe to access it.

Finalizer gets called doesn't mean memory will be reclaimed, If you do something like the following Finalize will be called but memory will not be reclaimed

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're dealing with managed code, It adds extra work to the garbage collector and also strange things can happen as we seen above.

Update: If you use _child only for lock it is safe to use, because the _child instance will not be null, which means it points to a valid reference. Monitor.Enter and Monitor.Exit just cares about the references it is absolutely safe to use it(only for locking).

What if you need the child's Finalizer to be called only after Test's Finalizer is called?

There is a workaround: You can inherit the Child class from SafeHandle and that does the trick. It will make sure if both Test and Child goes out of scope at the same time, It will call Test's finalizer first as the Child inherits from SafeHandle which delays its finalization. But, IMO don't depend on this. Because other programmers work with you may not be knowing this which leads to misconception.

This critical finalizer also has a weak ordering guarantee, stating that if a normal finalizable object and a critical finalizable object become unreachable at the same time, then the normal object’s finalizer is run first

Quote from SafeHandle: A Reliability Case Study

like image 110
Sriram Sakthivel Avatar answered Sep 07 '25 23:09

Sriram Sakthivel