[Mono-dev] Why does .NET object lifetime not extend into an instance method call?

Lepisto, Stephen P stephen.p.lepisto at intel.com
Fri Aug 24 20:47:47 UTC 2012


Am I wrong in thinking that in

   void Problem() { mo.doSomething(); }

"mo" is contained within the context of the method body of Problem() and therefore cannot be disposed of until the method body of Problem() has done execution.  This means that "mo" will continue to live for the life of the call to doSomething() because the Problem() method body holds onto "mo" until after doSomething() returns.


From: mono-devel-list-bounces at lists.ximian.com [mailto:mono-devel-list-bounces at lists.ximian.com] On Behalf Of David Jeske
Sent: Friday, August 24, 2012 1:27 PM
To: Jonathan Pryor
Cc: mono-devel-list at lists.ximian.com
Subject: Re: [Mono-dev] Why does .NET object lifetime not extend into an instance method call?

On Fri, Aug 24, 2012 at 10:50 AM, Jonathan Pryor <jonpryor at vt.edu<mailto:jonpryor at vt.edu>> wrote:
> It seems this could happen in more cases than just PInvoke. This seems to allow a finalizer to run before an object is "done being used" anytime the object instance is not stored. (i.e. inside a statement of the form "new Foo().Method();") If the finalizer triggers an IDispose pattern, this could cause a managed resource to be torn down before it's done being used as well.

The managed resource can't be disposed before it's done being used AS LONG AS the GC knows about all uses of the managed resource.

... snip ...

The real problem is that the GC doesn't know anything about native code, and thus can't ensure that no native code is using the resource.

Thanks very mych for the detailed reply. It seems to me there is a race that has nothing to do with native code. Consider this example..

class Foo : IDisposable {
   ManagedObject mo = new ManagedObject();

   ~Foo() { this.Dispose(); }
   public void Dispose() {
       if (mo != null) {
          try {mo.Dispose();} finally { mo = null; }
       }
   }

   void Problem() { mo.doSomething(); }

   static void Main() { new Foo().Problem(); }
}

If I understand the MS.NET<http://MS.NET> article, as soon as ms.doSomething enters the vcall, "this" is no longer referenced. Which means during ManagedObject.DoSomething, Foo could be finalized, and thus Disposed, and since the Dispose explicitly Disposes mo, the code would Dispose mo while it's still inside mo.doSomething(). Did I miss something?


> Why isn't this considered a bug in the .NET runtime?
How would you fix it? The .NET runtime has no way of knowing what native code is doing, so short of disassembling the native code ("magic"), what is .NET supposed to do?

Ohh, I don't think the problem is the way this is handled for native code. I think the above interaction in IDisposable seems like a problem too. To me this seems like a pre-mature finalization bug caused because "this" isn't considered referenced for the entire body of instance methods.

> (2) Does the Mono GC have the same behavior?
Yes, because there's no other sane behavior.

However, this can't be relied upon; Linux supports "precise stack marking," which prevents conservative scanning of native stack frames. This has the wonderful performance advantage that less memory needs to be pinned, allowing the GC to be more efficient:

        http://www.mono-project.com/Generational_GC#Precise_Stack_Marking

I'm sorry for my naivety. Why does allowing unused function arguments to be collected before a function returns have such important effects on memory usage?


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.ximian.com/pipermail/mono-devel-list/attachments/20120824/f35d5f4f/attachment-0001.html>


More information about the Mono-devel-list mailing list