我遇到了一个问题,即GC线程正在唤醒并在使用时删除对象。
当processfoo正在运行时,在它返回之前,会出现fooCopy析构函数在单独的线程中触发。有没有人看到这样的问题,如果是这样,你如何解决它?当然,这不可能真的发生,我必须做错事。谁能给我一些调试垃圾收集的提示/策略?
try
{
CFoo fooCopy = new CFoo(someFoo);
someBar.ProcessFoo(fooCopy);
}
CFoo有一个IntPtr成员变量。 ProcessFoo将该成员变量传递给C ++ DLL中的导入函数,该函数可能需要几秒钟才能运行。
在我的C ++ DLL中,我在创建IntPtr时以及删除它时进行记录,并且在ProcessFoo运行时我可以看到一个单独的线程删除IntPtr。
答案 0 :(得分:5)
当然,这不可能真的发生,我一定是做错了。
不,它可以绝对正在发生(这就是为什么写终结器很难,你应该尽可能地避免它们,并且在被迫写它们时要非常小心)。一旦运行时可以证明没有代码会再次尝试访问它,就可以对对象进行GC编辑。如果您调用实例方法(并且在调用后的任何时间都不会访问该对象实例),则该对象在该实例方法最后一次使用this
后立即有资格进行收集。
范围内的对象(例如,方法的this
变量)但在该范围中永远不会再次使用的对象不被GC视为。< / p>
至于你如何解决它,如果你有一个你想要被考虑的变量&#34; alive&#34;即使它再也没有在托管代码中访问过,请使用GC.KeepAlive
。在这种情况下,将GC.KeepAlive(this)
添加到ProcessFoo
的末尾将确保相关对象在方法结束之前保持活动状态(或者如果它不是该方法的责任) ,让来电者GC.KeepAlive(someBar)
之后立即致电ProcessFoo
。
有关此主题的更多信息,请参阅this blog post,以及终结器的一些相关甚至更不寻常的属性。
答案 1 :(得分:2)
当你与C ++交互时,这是很正常的,GC没有希望发现IntPtr正在其他任何地方使用。它不是GC可以识别的引用类型,也不能探测本机代码的堆栈帧。抖动标记使用中的fooCopy
对象引用最多基础CALL,而不是超出此范围。换句话说,当本机代码正在执行时,它有资格收集。如果另一个线程触发GC,那么它就是sayonora。
您可以在this post中找到有关本地变量生命周期的详细信息。
有几种可行的解决方法,尽管从问题中猜不到正确的解决方法。除了SafeHandle类之外,非常擅长确保完成定型,您可以在[DllImport]声明中使用HandleRef
而不是IntPtr。或者将GC.KeepAlive()
附加到此代码以强制抖动延长 fooCopy 的生命周期。