【发布时间】:2014-07-23 18:03:13
【问题描述】:
我的示例是在托管代码中使用非托管代码。
如果我在一个方法中实例化一个类级别的字段,GC 会在我的类的实例被收集之前收集这个字段,如下所示:
public class SpyAgent
{
public delegate int WindowProcedureDelegate(int Wnd, uint Msg, int WParam, int LParam);
private WindowProcedureDelegate _windowProcedure;
public void SpyAgent()
{
}
...
public void SomeRandomMethod()
{
...
//GC will collect this instance before SpyAgent dispose.
_windowProcedure = new WindowProcedureDelegate(WindowProcedure);
SetWindowLong(window.Handle, (IntPtr)_windowProcedureIndex, _windowProcedure);
}
[DllImport("user32.dll")]
private static extern IntPtr SetWindowLong(IntPtr hWnd, IntPtr nIndex, WindowProcedureDelegate newProc);
...
}
使用系统一分钟后,我收到以下错误:
检测到托管调试助手“CallbackOnCollectedDelegate” 一个问题 'C:\Acaz\Supervise.Bootstrap\Supervise.Bootstrap\Supervise.Bootstrap\Supervise.Bootstrap\Contoso\bin\Debug\Contoso.vshost.exe'。
附加信息:对类型为垃圾收集的委托进行了回调 'Supervise.Application!Supervise.Application.SpyAgent+WindowProcedureDelegate::Invoke'。 这可能会导致应用程序崩溃、损坏和数据丢失。什么时候 将委托传递给非托管代码,它们必须由 托管应用程序,直到保证它们永远不会 调用。
但是当我在构造函数中实例化委托时,我没有遇到更多错误。
public class SpyAgent
{
public delegate int WindowProcedureDelegate(int Wnd, uint Msg, int WParam, int LParam);
private WindowProcedureDelegate _windowProcedure;
public void SpyAgent()
{
//This instance will be collected by GC just when SpyAgent start to being collected too.
_windowProcedure = new WindowProcedureDelegate(WindowProcedure);
}
...
public void SomeRandomMethod()
{
...
SetWindowLong(window.Handle, (IntPtr)_windowProcedureIndex, _windowProcedure);
}
[DllImport("user32.dll")]
private static extern IntPtr SetWindowLong(IntPtr hWnd, IntPtr nIndex, WindowProcedureDelegate newProc);
...
}
为什么会有这种行为?为什么在收集类之前收集在方法中初始化的类级别的字段。没有意义,因为该字段是在类级别声明的,可以用于整个类。我将引用存储在类级项目中。
为什么当我在构造函数中初始化字段时,直到类也开始被收集时才收集字段?
垃圾收集器的行为与在方法和构造函数中初始化的对象有什么区别?
【问题讨论】:
-
您需要告诉我们您如何验证您所描述的事情确实发生了,因为那不是应该发生的。我认为您更有可能遇到不同的错误/问题,让您认为是这种情况。请告诉我们您做了什么来验证这一点。如果可能,您能否创建一个小而完整的程序,可以编译并运行来验证这一点?
-
说清楚,没有区别。假设您调用了实例化对象并将其存储到字段中的方法,它应该具有与在构造函数中分配和分配它的另一个示例完全相同的生命周期。
-
好的。我会将问题编辑为更完整的问题。
-
你能告诉我们你构造对象的代码吗? IE。间谍代理。
-
好吧,问题不在于对象被垃圾收集得太快,因为与构造函数相比,将引用从方法存储到字段中没有区别。一旦引用在现场,两种情况下的生命周期都是相同的。我认为您将其提供给 Win32 函数的方式更有可能存在问题。也许 .NET 没有正确包装它?或者您尝试执行的指定操作可能无法通过 C# 完成?
标签: c# .net memory-management garbage-collection