.NET GC 删除正在使用的对象

.NET GC deleting object in use

我运行遇到了一个问题,GC 线程似乎正在唤醒并删除正在使用的对象。

虽然 processfoo 是 运行ning,但在它之前 returns,它似乎会在单独的线程中触发 fooCopy 析构函数。有没有人见过这样的问题,如果有,你是如何解决的?当然,这不可能真的发生,我一定是做错了什么。谁能给我一些 tips/strategies 来调试垃圾收集?

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

CFoo 有一个 IntPtr 成员变量。 ProcessFoo 将该成员变量传递给从 C++ DLL 导入的函数,这可能需要几秒钟才能完成 运行。

在我的 C++ DLL 中,我记录 IntPtr 的创建时间和删除时间,我可以看到一个单独的线程在 ProcessFoo 正在 运行ning 时删除 IntPtr。

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

不,它 绝对 会发生(这就是为什么编写终结器很难,你应该尽可能避免使用它们,并且在被迫编写它们时要非常小心) .只要运行时可以证明没有代码会再次尝试访问对象,就可以对对象进行 GC 处理。如果您调用一个实例方法(并且在该调用之后任何时候都不会访问该对象实例),那么该对象在该实例方法最后一次使用 this 后立即符合收集条件。

范围内的对象(例如,方法的 this 变量)但在该范围内不再使用的对象 不被 GC 视为根对象.

至于你如何解决它,如果你有一个你想要被考虑的变量 "alive" 即使它再也不会在托管代码中访问,使用 GC.KeepAlive。在这种情况下,将 GC.KeepAlive(this) 添加到 ProcessFoo 的末尾将确保相关对象在方法结束之前保持活动状态(或者如果这不是该方法的责任,则让调用者调用 GC.KeepAlive(someBar)ProcessFoo 之后)。

有关此主题的更多信息,以及终结器的一些相关甚至更不寻常的属性,请参阅 this blog post

当您与 C++ 互操作时,这是很正常的,GC 没有希望发现 IntPtr 在其他任何地方正在使用。它不是 GC 知道的引用类型,也不能探测本机代码的堆栈帧。抖动标记 fooCopy 对象引用正在使用 底层 CALL,而不是超出此范围。换句话说,它有资格收集 本机代码正在执行。如果另一个线程触发了 GC 那么它就是 sayon​​ora。

您将在 this post 中找到有关局部变量生命周期的详细信息。

虽然无法从问题中猜出正确的解决方法,但有几种可能的解决方法。除了 SafeHandle 类,非常擅长确保最终化也得到处理,您可以在 [DllImport] 声明中使用 HandleRef 而不是 IntPtr。或者将GC.KeepAlive()附加到此代码以强制抖动延长fooCopy.

的生命周期