0

Consider the following code, where class A is responsible for managing native data. A pointer to that data is passed to a class B, so I need that A is not collected while B is in use:

class A
{
    public A(string n)
    {
        _n = n;
        _p = IntPtr.Zero; //acquire native resource
    }

    public IntPtr GetP()
    {
        return _p;
    }

    ~A()
    {
        Console.WriteLine("Free native resource {0}", _n);
    }

    string _n;
    IntPtr _p;
};

class B
{
    public B(IntPtr p)
    {
        _p = p;
    }

    public B(IntPtr p, Object o)
    {
        _p = p;
        _o = o;
    }

    public IntPtr GetP()
    {
        return _p;
    }

    IntPtr _p;
    Object _o;
};

If I don't hold a reference to 'a' from 'b', 'a' gets collected immediately as expected, which leaves me with a pointer to invalid data (at least in visual studio, the gc in mono is not so eager):

static void Main(string[] args)
{
    var a = new A("a");
    var b = new B(a.GetP());
    GC.Collect();
    GC.WaitForPendingFinalizers();
    Console.WriteLine("b._p: {0}", b.GetP());
}

Output:
Free native resource a
b._p: 0

But if I hold a reference to 'a' from 'b', 'a' is not freed until exit, even though 'a' is not used later on:

static void Main(string[] args)
{
    var a = new A("a");
    var b = new B(a.GetP(), a);
    GC.Collect();
    GC.WaitForPendingFinalizers();
    Console.WriteLine("b._p: {0}", b.GetP());
}

Output:
b._p: 0
Free native resource a

So my question is: in this second case, is 'a' guaranteed by spec to be alive while 'b' is not collected, or is this behavior implementation dependent? If the latter, how would I go about keeping it alive? I've seen several similar questions but I found no definitive answer for it.

page
  • 41
  • 1
  • I believe by spec it holds that if A is referenced in B, and B is alive, then A is alive also. I wouldn't be certain but if this were implementation dependent, the GC would implicate very strange and unexpected behavior. – Glubus May 18 '16 at 14:23
  • In your example `b` is not collected because you refer to it after GC.Collect, and `a` is collected because you don't. – mg30rg May 18 '16 at 14:23
  • `a` will *never* be collected as long as it is accessible (that is, *something* has a pointer to it). – Rob May 18 '16 at 14:24
  • Id expect and have always worked on the assumption that any reference to an object keeps it alive by default – BugFinder May 18 '16 at 14:25
  • Just as a side note - this design seems problematic for other reasons. Generally, if there's an unmanaged resource, you should design for exactly one class to work with e.g. `IntPtr`s related to it, and offer suitable abstractions to hide its existence, rather than having multiple classes that are sharing `IntPtr`s around – Damien_The_Unbeliever May 18 '16 at 14:31

1 Answers1

5

So my question is: in this second case, is 'a' guaranteed by spec to be alive while 'b' is not collected,

Yes. If b has a reference to a and is not collected, a will not be collected either.

Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758
  • 1
    This is a very fundamental guarantee. It is the principle of garbage collection. – Rob May 18 '16 at 14:25
  • @Rob (and maybe @Thomas), can you elaborate? How is this different from having a no longer used reference in scope? – page May 20 '16 at 08:23
  • @page It's a lot harder to guarantee that a variable is no longer going to be used, compared to going out of scope. I suppose it might be technically possible to detect variables still in scope, but can never be used - but that's probably way too much work and processing power for very little gain – Rob May 20 '16 at 08:34
  • @Rob, but the gc does collect objects still in scope but no longer used (see my first example). How is this different from a member that cannot be reached and is not used? – page May 20 '16 at 08:49
  • 1
    @Rob that's not always the case (I just tried and it changes if run from msvc, but not from cmd line). See Hans' excellent answer in this post [http://stackoverflow.com/questions/17130382/understanding-garbage-collection-in-net] – page May 20 '16 at 09:11
  • @Thomas, could you point me to a reference where this is stated or explained? – page May 20 '16 at 09:28