gpt4 book ai didi

c# - 循环中不可访问对象的垃圾收集

转载 作者:太空狗 更新时间:2023-10-30 00:51:58 25 4
gpt4 key购买 nike

如果我有这样一个循环:

public class Foo {
public Foo Foo;

public Foo() {
}
}

class Program {
public static void Main(string[] args) {
var foo = new Foo();
long i = 0;
while(i < Int64.MaxValue) {
foo.Foo = new Foo();
foo = foo.Foo;
if(i % 10000 == 0)
GC.Collect();
i++;
}
GC.Collect();
}
}

直到循环退出,垃圾收集器才会清理父对象。这是为什么?一旦 foo 被重新分配,我看不出有任何方法可以从代码中引用它们,所以不应该清理它们吗?

在通过我设置的一些断点以确定发生这种情况后,我正在任务管理器中查看进程的内存使用情况。它在循环内不断上升(如果我将其设为无限,则上升到数 GB),但在循环退出并调用第二个 GC.Collect() 时立即下降。

最佳答案

这是一个稍微修改过的程序,可以更清楚地演示该行为:

class Foo
{
public int Value;
public Foo Next;

public Foo(int value) { this.Value = value; Console.WriteLine("Created " + this.Value); }
~Foo() { Console.WriteLine("Finalized " + this.Value); }
}

class Program
{
public static void Main(string[] args)
{
var foo = new Foo(0);
for (int value = 1; value < 50; ++value)
{
foo.Next = new Foo(value);
foo = foo.Next;
if (value % 10 == 0)
{
Console.WriteLine("Collecting...");
GC.Collect();
Thread.Sleep(10);
}
}
Console.WriteLine("Exiting");
}
}

在 .NET 4.5 上,当我在 Debug模式下构建AND 目标任何 CPU 或 x86 时,我重现了您所看到的行为:在打印“Exiting”之前实例不会最终确定.但是当我在 Release模式目标 x64 中构建时(即使在 Debug模式下构建时),实例一旦无法访问就会完成:

Created 0
Created 1
Created 2
Created 3
Created 4
Created 5
Created 6
Created 7
Created 8
Created 9
Created 10
Collecting...
Finalized 9
Finalized 0
Finalized 8
Finalized 7
Finalized 6
Finalized 5
Finalized 4
Finalized 3
Finalized 2
Finalized 1
Created 11
Created 12
Created 13
...

为什么会这样?我想只有 CLR 专家才能确定地告诉我们,但这是我的猜测:行为取决于 JIT 编译器和优化器碰巧生成的机器代码的具体细节,这些细节因目标指令集以及您是否“重新以 Debug模式运行。 (此外,这些细节可能会在运行时的 future 版本中发生变化。)特别是在 x86/Debug 情况下,我认为第一个 Foo(0) 实例被隐藏在寄存器或堆栈变量中永远不会在方法的其余部分被覆盖;这个初始实例使整个链保持事件状态。在 x86/Release 和 x64 情况下,我认为由于 JIT 优化,相同寄存器或堆栈变量被每个实例重用,从而释放初始实例。

关于c# - 循环中不可访问对象的垃圾收集,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24153829/

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