Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/317.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# .NET4.5Async/Await和垃圾收集器_C#_.net_Garbage Collection_Async Await - Fatal编程技术网

C# .NET4.5Async/Await和垃圾收集器

C# .NET4.5Async/Await和垃圾收集器,c#,.net,garbage-collection,async-await,C#,.net,Garbage Collection,Async Await,我想知道async/await与垃圾收集局部变量相关的行为。在下面的示例中,我已经分配了相当大的内存部分,并进入了一个显著的延迟。如代码所示,等待后不使用缓冲区。它在等待时会被垃圾收集,还是在函数执行期间内存会被占用 /// <summary> /// How does async/await behave in relation to managed memory? /// </summary> public async Task<bool> Allocat

我想知道
async/await
与垃圾收集局部变量相关的行为。在下面的示例中,我已经分配了相当大的内存部分,并进入了一个显著的延迟。如代码所示,
等待
后不使用
缓冲区
。它在等待时会被垃圾收集,还是在函数执行期间内存会被占用

/// <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;
}
//
///async/await与托管内存的关系如何?
/// 
公共异步任务AllocateMemoryAndWaitForAWhile(){
//分配相当大的内存量。
var Buffer=新字节[32*1024*1024];
//显示缓冲区的长度(以避免优化删除)。
System.Console.WriteLine(缓冲区长度);
//毫无理由地等待一分钟。
等待任务。延迟(60000);
//“缓冲区”是否在等待时被garabage收集器释放?
返回true;
}
它会在等待时收集垃圾吗

也许吧。允许垃圾收集器这样做,但不要求它这样做

功能期间内存是否会被占用

/// <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;
}
也许吧。允许垃圾收集器这样做,但不要求它这样做

基本上,如果垃圾收集器知道缓冲区将永远不会被再次触及,那么它可以随时释放缓冲区。但是GC从来没有被要求在任何特定的时间表上释放任何东西

如果您特别关心,您可以始终将local设置为
null
,但除非您有明显的问题,否则我不会这么做。或者,您可以将操纵缓冲区的代码提取到它自己的非异步方法中,并从异步方法中同步调用它;然后局部成为普通方法的普通局部

wait
实现为
返回
,因此本地将超出范围,其生存期将结束;然后将在下一次采集时采集阵列,这需要在
延迟期间进行,对吗

不,这些说法都不是真的

首先,如果任务未完成,
wait
仅是一个
return
;现在,
Delay
几乎不可能完成,因此是的,这将返回,但我们不能得出结论,一般来说,
wait
返回给调用方

其次,只有当C#编译器在IL中实际将局部作为临时池中的局部实现时,局部才会消失。抖动会将其jit为堆栈插槽或寄存器,当方法的激活在
wait
处结束时,抖动消失。但是C#编译器不需要这样做

对于调试器中的人员来说,在
延迟之后放置断点并看到本地已消失似乎很奇怪,因此编译器可能会将本地实现为编译器生成的类中的一个字段,该类绑定到为状态机生成的类的生存期。在这种情况下,抖动不太可能意识到该字段不再被读取,因此不太可能过早地将其丢弃。(虽然允许这样做。而且,如果C#编译器可以证明您已使用该字段,它也可以代表您将该字段设置为null。同样,对于调试器中突然看到其本地更改值的人来说,这会很奇怪,因为没有明显的原因,但是编译器可以生成任何代码,这些代码的单个threa(我的行为是正确的。)

第三,没有任何东西需要垃圾收集器按照特定的时间表收集任何东西。这个大型数组将被分配到大型对象堆上,并且该对象有自己的收集计划


第四,任何东西都不需要在任何给定的六十秒间隔内收集大型对象堆。如果没有内存压力,就不需要收集这些东西。

我很确定它已收集,因为Wait结束了当前任务并“继续”了另一个任务, 因此,在等待后不使用时,应清洁本地VAR

但是:编译器的实际功能可能有所不同,因此我不会依赖于这种行为

它会在等待时收集垃圾吗

没有

功能期间内存是否会被占用

/// <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;
}

在Reflector中打开编译的程序集。您将看到编译器生成了一个从IAsyncStateMachine继承的私有结构,异步方法的局部变量是该结构的一个字段。当所属实例仍处于活动状态时,类/结构的数据字段永远不会被释放。

您的代码编译(在我的环境中:VS2012、C#5、.NET 4.5、发布模式)以包含实现
IAsyncStateMachine的结构,并且具有以下字段:

public byte[] <Buffer>5__1;
公共字节[]5_u_1;

因此,除非JIT和/或GC真的很聪明,(有关更多信息,请参阅),否则可以合理地假设大的
字节[]
将保持在作用域内,直到异步任务完成。

Eric Lippert所说的是正确的:C编译器在为
异步
方法生成什么IL方面有很大的回旋余地。因此,如果您询问规范对此有何规定,那么答案是:数组可能在等待期间符合收集条件,这意味着它可能被收集

    private static async Task MethodAsync()
    {
        byte[] bytes = new byte[1024];
        var wr = new WeakReference(bytes);

        Console.WriteLine(wr.Target != null);

        await Task.Delay(100);

        FullGC();

        Console.WriteLine(wr.Target != null);

        await Task.Delay(100);

    }

    private static void FullGC()
    {
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();
    }
 private static async Task MethodAsync()
    {
        byte[] bytes = new byte[1024];
        var wr = new WeakReference(bytes);

        Console.WriteLine(wr.Target != null);

        await Task.Delay(100);

        Console.WriteLine(bytes.Length);

        FullGC();

        Console.WriteLine(wr.Target != null);

        await Task.Delay(100);

        FullGC();

        Console.WriteLine(wr.Target != null);
    }
但另一个问题是编译器实际上做什么。在我的计算机上,编译器生成
Buffer
作为生成状态机类型的字段。该字段被设置为已分配的数组,然后再也不会设置。这意味着,当状态机对象符合条件时,数组将符合收集条件。并且该对象是从continuation委托引用的,因此在等待完成之前,它不符合收集条件。这一切都意味着数组不会