提问人:nrofis 提问时间:11/14/2023 更新时间:11/14/2023 访问量:58
是 GC。KeepAlive官方示例有效吗?
Is the GC.KeepAlive official example valid?
问:
我正在查看该方法的官方示例。但我不完全明白为什么那里是必要的。GC.KeepAlive
GC.KeepAlive
GC 收集所有不再具有引用的对象。根据我的理解,引用托管指针的变量在方法上下文的整个生命周期内都存在。因此,GC 永远不应该收集它。hr
Main
只有当它有一个额外的优化 (*) 时,GC 才会收集它,该优化知道即使引用了对象,将来也不会使用它。hr
我试图找到额外优化的证据,但没有找到。我删除了该命令,程序继续按预期运行(在发布版本中,完全优化,未附加调试器)。GC.KeepAlived
我能够通过将 decleration 移动到另一个生命周期较短的方法/上下文来收集 GC。hr
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
我的问题:
- 官方示例有效吗?因为它即使没有
GC.KeepAlive
- 额外的优化(上面标有(*))真的存在吗?如果是这样,在哪些 .NET 版本中?因为我看不到它(在 .NET 6 中测试)
答:
据我了解,引用托管指针的 hr 变量在 Main 方法上下文的整个生命周期内都存在。因此,GC 永远不应该收集它。
这是错误的。允许 GC 在最后一次使用引用后立即收集对象。完全有可能在运行同一对象的方法时收集对象。
只有当它有一个额外的优化 (*) 时,GC 才会收集它,它知道即使 hr 引用了该对象,它也不会在将来被使用。
这更正确,我不确定它是否在语言文档中指定。但是在KeepAlive的文档中对此进行了很好的解释。也就是说,GC 无法跟踪原生代码“拥有”的引用,因此可能会过早地收集对象。
我试图找到额外优化的证据,但没有找到。我删除了 GC。KeepAlived 命令,程序将继续按预期运行(在发布版本中,完全优化,未附加调试器)。
我的猜测是,这是由于自创建此示例以来 .Net 中的各种更改。运行时发生了很多可能影响集合的情况。我不能说变化的具体原因导致了这种情况,也可能不是完全确定的。
这不会使示例所要演示的原则无效,但如果该示例无法再演示实际问题行为,则确实会使该示例变得相当糟糕。
评论
hr
GC.KeepAlive(hr)
Main
method/context that has shorter lifespan.
GC.Collect()
hr
MyWin32.SetConsoleCtrlHandler(hr, true);
GC.KeepAlive(hr);
hr
hr
async