gpt4 book ai didi

c# - 垃圾收集异步方法

转载 作者:可可西里 更新时间:2023-11-01 08:10:53 32 4
gpt4 key购买 nike

我刚刚注意到关于垃圾收集的一些非常奇怪的事情。

WeakRef 方法按预期收集对象,而 async 方法报告对象仍然存在,即使我们已强制进行垃圾收集。有什么想法吗?

class Program
{
static void Main(string[] args)
{
WeakRef();
WeakRefAsync().Wait();
}

private static void WeakRef()
{
var foo = new Foo();
WeakReference fooRef = new WeakReference(foo);
foo = null;
GC.Collect();
Debug.Assert(!fooRef.IsAlive);
}

private static async Task WeakRefAsync()
{
var foo = new Foo();
WeakReference fooRef = new WeakReference(foo);
foo = null;
GC.Collect();
Debug.Assert(!fooRef.IsAlive);
}
}


public class Foo
{

}

最佳答案

The WeakRef method collects the object as expected

没有理由期待这一点。在 Linqpad 中尝试,它不会发生在调试版本中,例如,尽管调试版本和发布版本的其他有效编译可能有任何一种行为。

在编译器和抖动之间,它们可以自由地优化空赋值(毕竟在它之后没有使用 foo),在这种情况下,GC 仍然可以将线程视为具有引用对象而不是收集它。相反,如果没有 foo = null 的赋值,他们可以自由地意识到 foo 不再被使用,并重新使用曾经有过的内存或寄存器一直持有它以持有 fooRef(或者实际上完全是为了其他东西)并收集 foo

因此,无论有无 foo = null,GC 都可以将 foo 视为有根或无根,因此我们可以合理地预期这两种行为。

尽管如此,所看到的行为是合理可能会发生什么的期望,但并不能保证这一点值得指出。

好吧,撇开这个不谈,让我们看看这里实际发生了什么。

async 方法生成的状态机是一个结构,其字段对应于源中的局部变量。

所以代码:

var foo = new Foo();
WeakReference fooRef = new WeakReference(foo);
foo = null;
GC.Collect();

有点像:

this.foo = new Foo();
this.fooRef = new WeakReference(foo);
this.foo = null;
GC.Collect();

但是字段访问总是在本地发生一些事情。所以在这方面它几乎就像:

var temp0 = new Foo();
this.foo = temp0;
var temp1 = new WeakReference(foo);
this.fooRef = temp1;
var temp2 = null;
this.foo = temp2;
GC.Collect();

并且 temp0 没有被清空,所以 GC 发现 Foo 是 Root过的。

您的代码有两个有趣的变体:

var foo = new Foo();
WeakReference fooRef = new WeakReference(foo);
foo = null;
await Task.Delay(0);
GC.Collect();

和:

var foo = new Foo();
WeakReference fooRef = new WeakReference(foo);
foo = null;
await Task.Delay(1);
GC.Collect();

当我运行它时(同样,处理本地内存/寄存器的合理差异可能会导致不同的结果)第一个与您的示例具有相同的行为,因为当它调用另一个 Task 方法和 await 方法,该方法返回一个已完成的任务,因此 await 立即移动到同一底层方法调用中的下一个事情,即 GC.Collect().

第二个具有查看收集到的 Foo 的行为,因为 await 在该点返回,然后状态机有其 MoveNext() 方法在大约一毫秒后再次调用。由于这是对幕后方法的新调用,因此没有对 Foo 的本地引用,因此 GC 确实可以收集它。

顺便说一句,也有可能有一天编译器不会为那些不跨越 await 边界的本地人生成字段,这将是一种仍会产生正确行为的优化。如果发生这种情况,那么您的两种方法在潜在行为方面将变得更加相似,因此在观察到的行为方面更有可能相似。

关于c# - 垃圾收集异步方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42023239/

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