.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 那么它就是 sayonora。
您将在 this post 中找到有关局部变量生命周期的详细信息。
虽然无法从问题中猜出正确的解决方法,但有几种可能的解决方法。除了 SafeHandle 类,非常擅长确保最终化也得到处理,您可以在 [DllImport] 声明中使用 HandleRef
而不是 IntPtr。或者将GC.KeepAlive()
附加到此代码以强制抖动延长fooCopy.
的生命周期
我运行遇到了一个问题,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 那么它就是 sayonora。
您将在 this post 中找到有关局部变量生命周期的详细信息。
虽然无法从问题中猜出正确的解决方法,但有几种可能的解决方法。除了 SafeHandle 类,非常擅长确保最终化也得到处理,您可以在 [DllImport] 声明中使用 HandleRef
而不是 IntPtr。或者将GC.KeepAlive()
附加到此代码以强制抖动延长fooCopy.