我想知道 async/await
与垃圾收集局部变量相关的行为。在下面的示例中,我分配了相当大的内存部分并进入了显着的延迟。从代码中可以看出,在 await
之后没有使用 Buffer
。它会在等待时被垃圾收集,还是会在函数执行期间占用内存?
/// <summary>
/// How does async/await behave in relation to managed memory?
/// </summary>
public async Task<bool> AllocateMemoryAndWaitForAWhile() {
// Allocate a sizable amount of memory.
var Buffer = new byte[32 * 1024 * 1024];
// Show the length of the buffer (to avoid optimization removal).
System.Console.WriteLine(Buffer.Length);
// Await one minute for no apparent reason.
await Task.Delay(60000);
// Did 'Buffer' get freed by the garabage collector while waiting?
return true;
}
最佳答案
Will it get garbage collected while waiting?
也许吧。允许垃圾收集器这样做,但不是必须这样做。
Will the memory be occupied for the duration of the function?
也许吧。允许垃圾收集器这样做,但不是必须这样做。
基本上,如果垃圾收集器知道缓冲区将永远不会被再次触及,那么它可以随时释放它。但是 GC 永远需要按任何特定的时间表释放任何东西。
如果您特别担心,您始终可以将本地设置为 null
,但除非您确实遇到了问题,否则我不会费心这样做。或者,您可以将操作缓冲区的代码提取到它自己的非异步方法中,并从异步方法中同步调用它;然后本地就变成普通方法的普通本地。
The
await
is realized as areturn
, so the local will go out of scope and its lifetime will be over; the array will then be collected on the next collection, which is required to be during theDelay
, right?
不,这些说法都不是真的。
首先,如果任务未完成,await
只是一个return
;现在,当然 Delay
几乎不可能完成,所以是的,这会返回,但我们不能一般地断定 await
返回给调用者。
其次,仅当 C# 编译器在 IL 中将其实际实现为临时池中的本地时,本地才会消失。抖动会将其作为堆栈槽或寄存器进行抖动,当方法的激活在 await
结束时,它会消失。但是 C# 编译器不需要这样做!
对于调试器中的人来说,在 Delay
之后放置一个断点并看到本地已经消失,这似乎很奇怪,因此编译器可能将本地实现为编译器生成的类中的一个字段,它绑定(bind)到为状态机生成的类的生命周期。在那种情况下,抖动不太可能意识到这个字段再也不会被读取,因此也不太可能提前丢弃它。 (尽管允许这样做。而且 C# 编译器允许代表您将字段设置为 null,前提是它可以证明您已完成使用. 同样,这对于调试器中的人来说很奇怪,他们突然看到他们的本地更改值没有明显的原因,但允许编译器生成单线程行为正确的任何代码。)
第三,不需要垃圾收集器按照任何特定的时间表收集任何东西。这个大数组会分配到大对象堆上,那个东西有自己的回收时间表。
第四,没有任何东西要求在任何给定的六十秒间隔内存在大型对象堆的集合。如果没有内存压力,那东西永远不需要收集。
关于c# - .NET 4.5 Async/Await 和垃圾收集器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16598409/