简体   繁体   中英

.NET GC deleting object in use

I'm running into a problem where it would appear the GC thread is waking up and deleting an object while it's in use.

While processfoo is running, and before it returns, it would appear fooCopy destructor fires in a separate thread. Has anyone seen a problem like this and if so how do you work around it? Surely, this can't be really happening and I must be doing something wrong. Can anyone give me some tips/strategies to debug the garbage collection?

try
{
    CFoo fooCopy = new CFoo(someFoo);
    someBar.ProcessFoo(fooCopy);
}

CFoo has an IntPtr member variable. ProcessFoo passes that member variable to an imported function from a C++ DLL that might take a few seconds to run.

In my C++ DLL I log when the IntPtr is created and when it is deleted and I can see a separate thread deleting the IntPtr while ProcessFoo is running.

Surely, this can't be really happening and I must be doing something wrong.

Nope, it can absolutely be happening (this is why writing finalizers is hard, and you should avoid them where you can, and be very careful when forced to write them). An object can be GC-ed as soon as the runtime can prove that no code will ever try to access it again. If you call an instance method (and never access the object instance any time after that invocation) then the object is eligible for collection immediately after the last usage of this from that instance method.

Objects that are in scope (say, for example, the this variable of a method) but that are never used again within that scope are not considered rooted by the GC .

As for how you work around it, if you have a variable that you want to be considered "alive" even though it's never accessed in managed code again, use GC.KeepAlive . In this case, adding GC.KeepAlive(this) to the end of ProcessFoo will ensure that the object in question stays alive until the end of the method (or if it's not that method's responsibility, have the caller call GC.KeepAlive(someBar) right after ProcessFoo ).

See this blog post for more information on this topic, and a few related and even more unusual properties of finalizers.

This is pretty normal when you interop with C++, the GC has no hope of discovering that the IntPtr is in use anywhere else. It is not a reference type that the GC has awareness of, nor can it probe the stack frames of native code. The jitter marks the fooCopy object reference in use up to the underlying CALL, not beyond that. In other words, it is eligible for collection while the native code is executing. If another thread triggers a GC then it is sayonora.

You'll find details about the lifetime of local variables in this post .

There are several possible workarounds for this, albeit that the correct one can't be guessed from the question. Beyond the SafeHandle classes, very good at ensuring the finalization is taken care of as well, you could use HandleRef instead of IntPtr in the [DllImport] declaration. Or append GC.KeepAlive() to this code to force the jitter to extend the lifetime of fooCopy .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM