2014-01-02 2 views
2

Можете ли вы объяснить мне, что происходит в памяти при выполнении следующего кода:исполнение C# Ленивый + память пониманий

Случай 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); 
     } 
    } 
} 

Давайте предположим, что после первой итерации я получаю 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); 
     } 
    } 
} 

В моем понимании, Случай 1 лучше для памяти, то случай 2 верно?

В случае, если 2 по-прежнему будет ссылаться на тексты, которые мы уже скачали, в то время как в случае 1 каждый текст помечен для сбора мусора после его неиспользования. Я прав?

+0

«Предположим, что после первой итерации я получаю html1.» «Можете ли вы пояснить, что вы подразумеваете под« * get html1 * »? –

+0

рассмотрите html1 текст, загруженный и возвращенный в текст (текст var) внутри оператора foreach в функции выполнения –

+1

В дополнение к «когда» вас также может заинтересовать «почему»: http://startbigthinksmall.wordpress.com/ 2008/06/09/за кулисами-c-yield-keyword/ –

ответ

5
  • _urls останется на неопределенное время, так как он находится в поле, как кажется.
  • DownloadTexts() (итератор, возвращенный им) сохраняется до конца цикла.
  • WebClient и html он производит пребывание в живых для одной итерации. Если вы хотите знать абсолютное точное время жизни, вам нужно использовать Reflector и мысленно имитировать, где эта ссылка перемещается. Вы обнаружите, что используемый в цикле IEnumerator ссылается на него до начала следующей итерации.

Все объекты, которые не являются живыми, могут быть GC'ed. Это происходит всякий раз, когда 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 
+0

Рассмотрим следующий пример: вы загружаете 10000 результатов из api, что дает вам 1000 результатов за раз. В каждом результате есть поле под названием sum, и вы хотите суммировать это поле по всем результатам. Таким образом, каждая итерация 1000 предметов загружается в память, и после того, как сумма будет выполнена над ними, они будут отмечены как очищенные, это правильно? –

+0

После каждой итерации все предыдущие элементы будут выпущены. Объекты поддерживаются только ссылками. Где будет ссылка на эти предыдущие статьи? Нет места, где это может быть. Foreach over 'Enumerable.Range (0, int.MaxValue-1). Выберите (x => новая строка ('x', 1024))', чтобы увидеть, что я имею в виду. Это выделяет 2 ТБ памяти, но все работает нормально. – usr

+1

Удивительный, который действительно имеет большой смысл сейчас. Спасибо большое! –

0

Он будет очищен от памяти, когда сборщик мусора будет работать, и определит, что он больше не используется.

Значение будет больше не использоваться в тот момент, когда foreach вызывает вызов метода IEnumerator.MoveNext(). Итак, эффективно, № 1.

0

Он будет стерта из памяти, когда сборщик мусора будет чувствовать себя, как это делать.

Но отправной точкой является то, что код не содержит больше ссылок на экземпляр объекта. Таким образом, ответ в этом случае следующий: когда-нибудь после блока, в котором вы создали объект.

Уверены в GC, он хорошо справляется со своей работой.

Смежные вопросы