4

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?

avo
  • 10,101
  • 13
  • 53
  • 81
  • Is _child referenced anywhere outside of class `Test`? – Heinzi Aug 25 '14 at 07:03
  • @Heinzi, no, it's purely internal to `Test`. – avo Aug 25 '14 at 07:04
  • Based on [this question](http://stackoverflow.com/q/2688636/87698) I don't think it is safe to use `_child`. `_child` might be garbage-collected before `Test`. – Heinzi Aug 25 '14 at 07:20
  • Ok, I just did the test: http://stackoverflow.com/a/25480821/2674222 ? – avo Aug 25 '14 at 07:22
  • I think the safety would be dependent on what you want to do with the child object? – Mick Aug 25 '14 at 07:30
  • Normally if there's something that needs to be done before the object is finalised you would implement IDispose and ensure referencing code always disposes the object, a good method is to enclose it in a using statement. I wouldn't use GCHandle. – Mick Aug 25 '14 at 07:31
  • @Mick, `_child` is referenced in a method which is called from the finalizer to access an unmanaged resource. That method does lock `(_child) { ... }`. – avo Aug 25 '14 at 07:46
  • Unmanaged resources ? Sounds like you really should be implementing IDispose. http://stackoverflow.com/questions/18336856/implementing-idisposable-correctly – Mick Aug 25 '14 at 07:51
  • @avo It would be better if you show some code instead of `{ ... }` to let us know what is happening in the background.? – Sriram Sakthivel Aug 25 '14 at 08:04
  • I'd suggest you're in big trouble if you really need access to the unmanaged resources within _child from the finalize of the parent "Test" class. Those resources should be released in the finalize of the child and shouldn't be available in the finalize of the Test class – Mick Aug 25 '14 at 08:18
  • @Mick, `_child` is only ever used as `lock (_child) { ... }`. But inside the `lock` I do access an unamanaged resource. – avo Aug 25 '14 at 08:19
  • Well difficult to comment without understanding the context or being able to see the code. I'd suggest during Finalize all operations / responsibility for childs unmanaged resources should be within the child's implementation of IDisposeable/Finalize. You shouldn't be doing anything in a class which requires access to the unmanaged resources owned by another class during the finalize. – Mick Aug 25 '14 at 08:26
  • If you're just using _child for the lock, there should be no problem with that, there'll be no problems with using the reference. The garbage collector will not do anything until after finalize has completed for all the objects in the graph being collected. – Mick Aug 25 '14 at 23:58

2 Answers2

2

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

Sriram Sakthivel
  • 72,067
  • 7
  • 111
  • 189
  • It appears this is wrong, or am I missing something: http://stackoverflow.com/a/25480821/2674222 ? – avo Aug 25 '14 at 07:21
  • I'm accepting this answer in its new edition @SriramSakthivel. – avo Aug 25 '14 at 09:11
  • 1
    @avo Your follow up link leads to recursion. Anyways [I got it](http://stackoverflow.com/questions/25482453/why-weakreference-isalive-becomes-false) :) – Sriram Sakthivel Aug 25 '14 at 09:13
2

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

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

In short, quoting from the posting:

You can not access any objects your object refer to, that has finalizers, as you have no guarantee that these objects will be in a usable state when your finalizer runs. The objects will still be there, in memory, and not collected, but they may be closed, terminated, finalized, etc. already.

You can however, use any other reachable object (with no finalization) safely during an object's finalization stage, e.g. a string. In practical terms this means it is safe to use any reachable object that does NOT implement the IDisposable interface.

Community
  • 1
  • 1
whale70
  • 333
  • 2
  • 9