gpt4 book ai didi

c# - 了解局部变量的垃圾收集器行为

转载 作者:行者123 更新时间:2023-11-30 15:30:15 33 4
gpt4 key购买 nike

下面是一个非常简单的控制台应用程序(试试 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();
}
}

这里,callback 对象在超出 Test 方法的范围之前不会被垃圾回收。

我认为 GC.KeepAlive(callback) 需要让它在 Test 中保持事件状态(如 MSDN suggests ),但显然它不是(在上面的代码)。

现在,如果我像下面这样更改代码,回调 会按预期进行垃圾回收:

Callback callback = new Callback();
callback = null;

.NET 4.5.1 会发生这种情况。

问题:我错过了什么吗?我可以依靠这种行为,还是特定于 .NET 版本的东西?

最佳答案

@Porges ' 评论很好地解释了一切:

Try building & running it in Release mode, without the debugger attached. I get the expected behaviour there but not in Debug.
...
ie. running with Ctrl-F5, not just F5. It collects it instantly for me in each of .NET 4/4.5/4.5.1. But yes, you can't really rely on this behaviour.

发布版本和 Ctrl-F5 恢复了预期的行为。 我敦促@Porges 将此作为答案发布,我会投票并接受并表示感谢。

作为后续,我想介绍以下有趣的行为。现在使用 Release + Ctrl-F5,即使我取消注释代码中的 //GC.KeepAlive(callback) 行,callback 仍然会被垃圾收集。显然,这是因为编译器将此行识别为由于 while (true) 循环而无法访问,并且仍然不会在 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 在这里非常重要,可以防止上述任何优化。

关于c# - 了解局部变量的垃圾收集器行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22495819/

33 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com