当前位置: 首页 > 编程语言 > C#

了解局部变量共享的垃圾收集器行为

时间:2023-04-11 00:26:51 C#

了解局部变量的垃圾收集器行为这是一个非常简单的控制台应用程序(尝试小提琴):usingSystem;使用系统线程;使用系统。线程.任务;公共类ConsoleApp{classCallback{publicCallback(){}~Callback(){Console.WriteLine("~Callback");}}staticvoidTest(CancellationTokentoken){回调callback=newCallback();while(true){token.ThrowIfCancellationRequested();//对于GCGC.Collect(GC.MaxGeneration,GCCollectionMode.Forced);GC.WaitForPendingFinalizers();线程.睡眠(100);}//不需要KeepAlive?//GC.KeepAlive(回调);}publicstaticvoidMain(){varcts=newCancellationTokenSource(3000);尝试{测试(cts.Token);}catch(Exceptionex){Console.WriteLine(ex.Message);}GC.Collect(GC.MaxGeneration,GCCollectionMode.Forced,true);GC.WaitForPendingFinalizers();Console.WriteLine("输入退出...");控制台.ReadLine();在这里,回调对象将不会被垃圾回收,直到它超出Test方法的范围。我认为GC.KeepAlive(callback)需要在测试时保持活动状态(如MSDN所建议的那样),但显然不是(在上面的代码中已注释掉)。现在,如果我更改下面的代码,回调将按预期进行垃圾回收:Callbackcallback=newCallback();回调=空;这发生在.NET4.5.1中。问题:我错过了什么吗?我可以依赖这种行为,还是特定于.NET版本的行为?@Porges的评论很好地解释了一切:尝试在没有附加调试器的情况下在发布模式下构建和运行它。我得到了预期的行为,但不是在调试中。…现在。使用Ctrl-F5运行,而不仅仅是F5。它会立即在.NET4/4.5/4.5.1中为我收集它。但是,是的,您不能真正依赖这种行为。发布版本和Ctrl-F5带回预期的行为。我敦促@Porges将此作为答案发布,我会投票并接受它,谢谢。作为后续,我想介绍以下有趣的行为。现在使用Release+Ctrl-F5,即使我取消注释代码中的//GC.KeepAlive(callback)行,回调仍然会被垃圾收集。显然,这是因为编译器将此行识别为由于while(true)循环而无法访问,并且仍然没有对回调发出强引用。以下是正确的模式:staticvoidTest(CancellationTokentoken){Callbackcallback=newCallback();尝试{while(true){token.ThrowIfCancellationRequested();//对于GCGC.Collect(GC.MaxGeneration,GCCollectionMode.Forced);GC.WaitForPendingFinalizers();线程.睡眠(100);}}最后{GC.KeepAlive(回调);查看GC.KeepAlive实现也很有趣:在调用KeepAlive之前持有对对象的强引用的IL代码。MethodImplOptions.NoInlining在这里非常重要,可以防止像上面那样的任何优化。.NET垃圾收集是不确定的。您链接到的MSDN页面说明了一切-强调:KeepAlive方法的目的是确保存在对垃圾收集器可能过早回收的对象的引用。仅仅因为回调可以在范围从Test返回之前被垃圾收集并不意味着它会。以上就是C#学习教程:了解局部变量的垃圾收集器行为分享的全部内容。如果对你有用,需要进一步了解C#学习教程,希望大家多多关注。本文收集自网络,不代表立场。涉及侵权,请点击维权联系管理员删除。如需转载请注明出处: