C#懒惰执行+内存理解

Ami*_*tan 2 c# memory lazy-evaluation yield-return

你可以告诉我在执行以下代码时内存中发生了什么:

情况1:

public static void Execute()
{
    foreach(var text in DownloadTexts())
    {
          Console.WriteLine(text);
    }
}


public static IEnumerable<string> DownloadTexts()
{
     foreach(var url in _urls)
     {
         using (var webClient = new WebClient())
         {
              yield return webClient.DownloadText(url);
         }
     }
}
Run Code Online (Sandbox Code Playgroud)

让我们假设在第一次迭代后我得到html1.

什么时候从内存中清除html1?

  1. 在下一次迭代?
  2. 当foreach结束?
  3. 当功能结束?

谢谢

**编辑**

案例2:

public static void Execute()
{
    var values = DownloadTexts();
    foreach(var text in values)
    {
          Console.WriteLine(text);
    }
}


public static IEnumerable<string> DownloadTexts()
{
     foreach(var url in _urls)
     {
         using (var webClient = new WebClient())
         {
              yield return webClient.DownloadText(url);
         }
     }
}
Run Code Online (Sandbox Code Playgroud)

根据我的理解,案例1对记忆更好,然后案例2对吗?

在案例2中,仍然会保留对我们已经下载的文本的引用,而在案例1中,每个文本在未使用后都标记为垃圾回收.我对么?

usr*_*usr 5

  • _urls 将无限期地停留,因为它看起来像是在一个领域.
  • DownloadTexts() (由它返回的迭代器)保持活动直到循环结束.
  • WebClienthtml它产生活着的一个迭代.如果您想知道它的绝对精确寿命,您需要使用Reflector并在心理上模拟参考传播的位置.你会发现IEnumerator循环中使用的引用它直到下一次迭代开始.

所有不活动的对象都可以进行GC操作.只要GC认为这是一个好主意,就会发生这种情况.

关于你的编辑:案例是等价的.如果不将枚举数放入变量中,编译器将为您执行此操作.它必须保持引用直到循环结束.有多少引用并不重要.至少有一个.

实际上,循环只需要使枚举器保持活动状态.您添加的其他变量也将使可枚举值保持活动状态.另一方面,您没有使用变量,因此GC不会使其保持活动状态.

您可以轻松测试:

//allocate 1TB of memory:
var items =
    Enumerable.Range(0, 1024 * 1024 * 1024)
    .Select(x => new string('x', 1024));
foreach (var _ in items) { } //constant memory usage
Run Code Online (Sandbox Code Playgroud)