我正在查看GC.KeepAlive
方法的官方示例。但我不完全理解为什么GC.KeepAlive
在那里是必要的。
GC收集所有没有更多引用的对象。据我所知,引用托管指针的hr
变量在Main
方法上下文的整个生命周期中都存在。因此,GC永远不应该收集它。
GC将收集它,只有它有一个额外的优化(*),知道即使hr
引用该对象,它在未来也不会被使用。
我试图找到额外优化的证据,但没有找到。我删除了GC.KeepAlived
命令,程序继续按预期运行(在发布版本中,完全优化,没有附加调试器)。
我能够通过将hr
声明移动到另一个寿命较短的方法/上下文来使GC收集hr
。
public static void Register()
{
MyWin32.HandlerRoutine hr = new MyWin32.HandlerRoutine(Handler);
MyWin32.SetConsoleCtrlHandler(hr, true);
}
public static void Main()
{
// Use interop to set a console control handler.
Register();
// Give the user some time to raise a few events.
Console.WriteLine("Waiting 30 seconds for console ctrl events...");
// The object hr is not referred to again.
// The garbage collector can detect that the object has no
// more managed references and might clean it up here while
// the unmanaged SetConsoleCtrlHandler method is still using it.
// Force a garbage collection to demonstrate how the hr
// object will be handled.
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
Thread.Sleep(30000);
// Display a message to the console when the unmanaged method
// has finished its work.
Console.WriteLine("Finished!");
Console.Read();
}
字符串
现在,没有GC.KeepAlive
,触发事件时会出现错误。但是一旦我在Main
方法的上下文中持有堆中委托的指针,没有KeepAlive
就不会出现错误。
我的问题:
1.官方的例子有效吗?因为它即使没有GC.KeepAlive
也能工作。
1.额外的优化(上面标有(*))是否真的存在?如果存在,在哪个.NET版本中?因为我看不到它(在.NET 6中测试)
1条答案
按热度按时间evrscar21#
据我所知,hr变量(它引用托管指针)在Main方法上下文的整个生命周期内都存在。因此,GC不应该收集它。
这是错误的。只要引用被最后一次使用,GC就可以收集对象。完全有可能在同一对象的方法正在运行时收集该对象。
GC会收集它,只有当它有一个额外的优化(*),知道即使hr引用该对象,它在未来也不会被使用。
这是更正确的,我不确定它是否在语言文档中指定。但它在KeepAlive的文档中有相当好的解释。即GC无法跟踪本机代码“拥有”的引用,因此可能会过早收集对象。
我试图找到额外优化的证据,但没有找到。我删除了GC. KeepAlive命令,程序继续按预期运行(在发布版本中,完全优化,没有附加调试器)。
我的猜测是,这是由于.Net中的各种变化,因为这个例子被创建。有发生了很多的运行时,可能会影响收集。我不能说什么具体的原因变化导致这一点,它也可能不是完全确定的。
这并不会使这个例子所要演示的原理无效,但是如果这个例子不能再演示实际的问题行为,它确实会使这个例子变得相当糟糕。