.net GC.KeepAlive官方示例有效吗?

f87krz0w  于 2023-11-20  发布在  .NET
关注(0)|答案(1)|浏览(125)

我正在查看GC.KeepAlive方法的官方示例。但我不完全理解为什么GC.KeepAlive在那里是必要的。
GC收集所有没有更多引用的对象。据我所知,引用托管指针的hr变量在Main方法上下文的整个生命周期中都存在。因此,GC永远不应该收集它。
GC将收集它,只有它有一个额外的优化(*),知道即使hr引用该对象,它在未来也不会被使用。
我试图找到额外优化的证据,但没有找到。我删除了GC.KeepAlived命令,程序继续按预期运行(在发布版本中,完全优化,没有附加调试器)。
我能够通过将hr声明移动到另一个寿命较短的方法/上下文来使GC收集hr

  1. public static void Register()
  2. {
  3. MyWin32.HandlerRoutine hr = new MyWin32.HandlerRoutine(Handler);
  4. MyWin32.SetConsoleCtrlHandler(hr, true);
  5. }
  6. public static void Main()
  7. {
  8. // Use interop to set a console control handler.
  9. Register();
  10. // Give the user some time to raise a few events.
  11. Console.WriteLine("Waiting 30 seconds for console ctrl events...");
  12. // The object hr is not referred to again.
  13. // The garbage collector can detect that the object has no
  14. // more managed references and might clean it up here while
  15. // the unmanaged SetConsoleCtrlHandler method is still using it.
  16. // Force a garbage collection to demonstrate how the hr
  17. // object will be handled.
  18. GC.Collect();
  19. GC.WaitForPendingFinalizers();
  20. GC.Collect();
  21. Thread.Sleep(30000);
  22. // Display a message to the console when the unmanaged method
  23. // has finished its work.
  24. Console.WriteLine("Finished!");
  25. Console.Read();
  26. }

字符串
现在,没有GC.KeepAlive,触发事件时会出现错误。但是一旦我在Main方法的上下文中持有堆中委托的指针,没有KeepAlive就不会出现错误。
我的问题:
1.官方的例子有效吗?因为它即使没有GC.KeepAlive也能工作。
1.额外的优化(上面标有(*))是否真的存在?如果存在,在哪个.NET版本中?因为我看不到它(在.NET 6中测试)

evrscar2

evrscar21#

据我所知,hr变量(它引用托管指针)在Main方法上下文的整个生命周期内都存在。因此,GC不应该收集它。
这是错误的。只要引用被最后一次使用,GC就可以收集对象。完全有可能在同一对象的方法正在运行时收集该对象。
GC会收集它,只有当它有一个额外的优化(*),知道即使hr引用该对象,它在未来也不会被使用。
这是更正确的,我不确定它是否在语言文档中指定。但它在KeepAlive的文档中有相当好的解释。即GC无法跟踪本机代码“拥有”的引用,因此可能会过早收集对象。
我试图找到额外优化的证据,但没有找到。我删除了GC. KeepAlive命令,程序继续按预期运行(在发布版本中,完全优化,没有附加调试器)。
我的猜测是,这是由于.Net中的各种变化,因为这个例子被创建。有发生了很多的运行时,可能会影响收集。我不能说什么具体的原因变化导致这一点,它也可能不是完全确定的。
这并不会使这个例子所要演示的原理无效,但是如果这个例子不能再演示实际的问题行为,它确实会使这个例子变得相当糟糕。

相关问题