c# - .NET 4.x : how to force full GC collection?

标签 c# .net garbage-collection

在调试配置中内置或附加调试器时,似乎某些更新改变了 GC 行为:

//Code snippet 1
var a = new object();
var w = new WeakReference(a);
a = null;

GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
Console.WriteLine(w.IsAlive ? "Alive" : "Dead");

这样的代码用来打印Dead ,并且它对于编写单元测试来检查应该进行 GC 的某些部分是否未被保留非常方便。

在进行一些 .NET 4.x 更新后,此代码在 .NET 2.x 和 3.x 上成功通过,但在 4.x 的所有变体上失败。我试图将其称为 GC.Collect(2, GCCollectionMode.Forced, blocking: true) ,制作<gcConcurrent enabled="false"/>App.configGCSettings.LatencyMode = GCLatencyMode.Batch - 没有任何帮助。如果我在没有附加调试器的情况下运行代码,并且它是在发布配置中构建的(即带有优化) - 它会输出 Dead 。否则是 Alive .

我知道在生产中依赖 GC 并不是一个好主意。但对于测试,我不知道如何替换通过测试检查特定代码片段不会泄漏内存的能力。这是纯粹的测试组装,我可以调整一些兼容性开关或类似的东西。我的目标是检查我自己的代码,而不是 GC 优化。

有没有办法以某种方式强制 GC 执行之前的行为?

附注我看到几乎相同的question ,但当时与NCrunch有关。我没有安装它。我什至从命令行运行了代码,根本没有 VS,也得到了相同的结果。


UPD:我发现,如果我将分配并设置对 null 的引用的代码移动到单独的方法中 - 它始终输出 Dead不过。

//Code snippet 2
internal class Program
{
    private static void Main(string[] args)
    {
        var w = DoWorkAndGetWeakRef();

        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();
        Console.WriteLine(w.IsAlive ? "Alive" : "Dead");
        Console.ReadLine();
    }

    private static WeakReference DoWorkAndGetWeakRef()
    {
        var a = new object();
        var w = new WeakReference(a);
        a = null;
        return w;
    }
}

如果我转移到单独的方法 GC 收集调用和 WeakReference 检查,结果相同:

//Code snippet 3
internal class Program
{
    private static void Main(string[] args)
    {
        var a = new object();
        var w = new WeakReference(a);
        a = null;

        CollectAndCheckWeakRef(w);
        Console.ReadLine();
    }

    private static void CollectAndCheckWeakRef(WeakReference w1)
    {
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();
        Console.WriteLine(w1.IsAlive ? "Alive" : "Dead");
    }
}

重要的一点似乎是原始的 w变量不在当前范围内。如果我搬家Console.WriteLine(w1.IsAlive ? "Alive" : "Dead");返回Main - 它变成Alive再次。

这两种变体有时都不是很方便,但至少是一致的( DebugRelease 配置,调试器是否附加 - 仍然输出 Dead )。

现在我很好奇,当前执行作用域中仅仅存在 WeakReference 变量如何会阻止 GC 清理其目标,以及为什么将其埋在调用堆栈中的作用域中的某个位置不会起到同样的作用。

最佳答案

在 Debug模式下,这应该使对象在所有 .NET 版本中保持事件状态。其他任何内容都是错误(或缺失的功能)。

您可以通过将一些代码拆分为新的方法来禁用此调试辅助。

在 Release模式下,这应该表现出您想要的短 GC 生命周期。当然不能保证这一点,但这是一个非常理想的优化。

另一种解决方法是使用新对象[1]或类似的构造。然后,您可以可靠地清空第一个数组成员。我认为该框架有一个 BoxStrongBox 类型。不知道叫什么。

关于c# - .NET 4.x : how to force full GC collection?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32876653/

相关文章:

c# - GridView 链接列值

c# - UriBuilder 无法对 UriBuilder 注册标志进行编码

c# - 是否收集了不安全的 C# 垃圾

c# - ComputeHash 调用莫名其妙地不同

c# - MeasureString() 为不同的屏幕分辨率提供不同的大小

c# - 仅当我指定 commandType 时才出现错误,无法使用 Dapper 和 c# 找到存储过程

.net - 如何在 VB.NET 中创建命令链接按钮(具有多行文本)?

c# - 软件包与 netcoreapp2.0 不兼容

.net - .net终结器是否始终执行?

c++ - 我如何试验垃圾收集?