问题描述
下面是一个非常简单的控制台应用程序(试行):
Below is a very simple console app (try the fiddle):
using System;
using System.Threading;
using System.Threading.Tasks;
public class ConsoleApp
{
class Callback
{
public Callback() { }
~Callback() { Console.WriteLine("~Callback"); }
}
static void Test(CancellationToken token)
{
Callback callback = new Callback();
while (true)
{
token.ThrowIfCancellationRequested();
// for the GC
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
GC.WaitForPendingFinalizers();
Thread.Sleep(100);
}
// no need for KeepAlive?
// GC.KeepAlive(callback);
}
public static void Main()
{
var cts = new CancellationTokenSource(3000);
try
{
Test(cts.Token);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true);
GC.WaitForPendingFinalizers();
Console.WriteLine("Enter to exit...");
Console.ReadLine();
}
}
在这里,回调
对象没有得到垃圾收集,直到它超出的测试
法的范围。
Here, the callback
object doesn't get garbage-collected until it goes out of scope of the Test
method.
我觉得 GC.KeepAlive(回调)
将需要保持它活着里面测试
(如 MSDN建议),但显然它不是(在上面的代码中注释掉)。
I thought GC.KeepAlive(callback)
would be required to keep it alive inside Test
(as MSDN suggests), but apparently it is not (commented out in the code above).
现在如果我改变像下面的代码中,回调
并获得垃圾回收,符合市场预期:
Now if I change the code like below, the callback
does get garbage-collected, as expected:
Callback callback = new Callback();
callback = null;
这情况与.NET 4.5.1。
This happens with .NET 4.5.1.
问题:我缺少的东西吗? ?我可以依靠这种行为,或者是它的东西.NET版本的具体
The question: Am I missing something? Can I rely upon this behavior, or is it something .NET version specific?
推荐答案
的的评论解释一切非常好:
@Porges' comments explain everything very well:
尝试建立和放大器;它运行在Release模式,不附加调试器。我得到预期的行为存在,但不是在调试。结果
...结果
IE浏览器。使用Ctrl-F5,而不只是F5运行。它收集它立即对我来说在每一个.NET 4 / 4.5 / 4.5.1。但是,是的,你真的不能依靠这种行为
发布版本和控制骨节病> - F5 骨节病>带回预期的行为。 我敦促@Porges张贴此作为一个答案,这是我想了票,并感谢接受的。
The Release build and - brought back the expected behavior. I urge @Porges to post this as an answer, which I'd up-vote and accept with thanks.
作为后续,我想拥有以下有趣的行为。现在,随着版本+ Ctrl键F5,即使我取消注释 // GC.KeepAlive(回调)
线在我的代码中,回调
仍然得到垃圾收集。显然,这是因为编译器可识别这条线不可达由于,而(真)
循环,仍然没有发出关于回调
As a follow-up, I'd like to feature the following interesting behavior. Now with Release + Ctrl-F5, even if I un-comment the // GC.KeepAlive(callback)
line in my code, the callback
still gets garbage-collected. Apparently, this is because the compiler recognizes this line as unreachable due to while (true)
loop and still doesn't emit a strong reference on callback
.
下面是正确的方式:
static void Test(CancellationToken token)
{
Callback callback = new Callback();
try
{
while (true)
{
token.ThrowIfCancellationRequested();
// for the GC
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
GC.WaitForPendingFinalizers();
Thread.Sleep(100);
}
}
finally
{
GC.KeepAlive(callback);
}
}
这也是有趣的看 GC.KeepAlive
实施
[MethodImpl(MethodImplOptions.NoInlining)]
public static void KeepAlive(object obj)
{
}
正如预期的那样,它什么也不做,只是服务器以提示编译器生成IL代码这使强大的参考对象,最多到的KeepAlive
调用点。 MethodImplOptions.NoInlining
在这里,以防止类似上述任何优化密切相关。
As expected, it does nothing and merely servers as a hint to the compiler to generate IL code which keeps a strong reference to the object, up to the point where KeepAlive
is called. MethodImplOptions.NoInlining
is very relevant here to prevent any optimizations like above.
这篇关于为一个局部变量理解的垃圾收集器的行为的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!