Является ли утечка памяти, если MemoryStream в .NET не закрыт?

У меня есть следующий код:

MemoryStream foo(){
    MemoryStream ms = new MemoryStream();
    // write stuff to ms
    return ms;
}

void bar(){
    MemoryStream ms2 = foo();
    // do stuff with ms2
    return;
}

Есть ли вероятность того, что выделенная память MemoryStream каким-то образом не удастся удалить позже?

У меня есть экспертный эксперт, настаивающий на том, что я вручную закрываю это, и я не могу найти информацию, чтобы сказать, имеет ли он действительную точку или нет.

Ответ 1

Если что-то одноразовое, вы всегда должны утилизировать его. Вы должны использовать оператор using в вашем методе bar(), чтобы убедиться, что ms2 удаляется.

В конце концов он будет очищен сборщиком мусора, но всегда полезно вызывать Dispose. Если вы запустите FxCop для своего кода, он пометит его как предупреждение.

Ответ 2

Вы не будете пропускать что-либо - по крайней мере, в текущей реализации.

Вызов Dispose не будет быстрее очищать память, используемую MemoryStream. Это остановит ваш поток от жизнеспособности для вызовов "Чтение/Запись" после вызова, что может быть или не быть полезным для вас.

Если вы абсолютно уверены, что никогда не хотите переходить с MemoryStream на другой поток, вам не причинит никакого вреда, чтобы не вызвать Dispose. Тем не менее, это, как правило, хорошая практика отчасти потому, что если вы когда-либо делаете изменения, чтобы использовать другой поток, вы не хотите, чтобы его укусил труднодоступный баг, потому что вы выбрали легкий выход на ранней стадии. (С другой стороны, есть аргумент YAGNI...)

Другая причина сделать это в любом случае заключается в том, что новая реализация может вводить ресурсы, которые будут освобождены в Dispose.

Ответ 3

Да, есть утечка, в зависимости от того, как вы определяете LEAK и насколько LATER вы имеете в виду...

Если по утечке вы имеете в виду "память остается выделенной, недоступной для использования, даже если вы ее используете", а последним вы имеете в виду в любое время после вызова dispose, тогда тогда может произойти утечка, хотя ее не постоянная (т.е. для жизни исполняемых приложений).

Чтобы освободить управляемую память, используемую MemoryStream, , вам нужно не ссылаться на нее, путем аннулирования вашей ссылки на нее, поэтому она сразу же становится доступной для сбора мусора. Если вы этого не сделаете, вы создадите временную утечку с того момента, когда вы ее используете, пока ваша ссылка не выйдет из области видимости, потому что пока выделение не будет доступно для размещения.

Преимущество используемого оператора (поверх простого вызова dispose) заключается в том, что вы можете ЗАКЛЮЧИТЬ свою ссылку в инструкции using. Когда завершающий оператор использования заканчивается, вызывается не только вызов, но и ваша ссылка выходит за пределы области действия, что фактически сводит на нет ссылку и делает ваш объект подходящим для сбора мусора немедленно, не требуя, чтобы вы запомнили код "reference = null".

Невзирая на то, что что-то сразу не является чем-то вроде классической "постоянной" утечки памяти, это определенно имеет тот же эффект. Например, если вы сохраняете свою ссылку на MemoryStream (даже после вызова dispose) и немного дальше в своем методе, вы пытаетесь выделить больше памяти... память, используемая вашим потоком памяти, не связанная с памятью, будет недоступна до тех пор, пока вы не аннулируете ссылку или не выйдете из области действия, даже если вы вызвали удаление и выполнили ее.

Ответ 4

Это уже ответили, но я просто добавлю, что хороший старомодный принцип скрытия информации означает, что вы в какой-то момент захотите реорганизовать:

MemoryStream foo()
{    
    MemoryStream ms = new MemoryStream();    
    // write stuff to ms    
    return ms;
}

в

Stream foo()
{    
   ...
}

Это подчеркивает, что вызывающим абонентам не стоит беспокоиться о том, какой поток возвращается, и позволяет изменить внутреннюю реализацию (например, когда насмехается для модульного тестирования).

Вам тогда понадобятся проблемы, если вы не использовали Dispose в своей реализации:

void bar()
{    
    using (Stream s = foo())
    {
        // do stuff with s
        return;
    }
}

Ответ 5

Вызов .Dispose() (или обертка с помощью Using) не требуется.

Причина, по которой вы вызываете .Dispose(), - освободить ресурс как можно скорее.

Подумайте, например, о сервере, где у нас ограниченный набор памяти и тысячи запросов. Мы не хотим ждать запланированной сборки мусора, мы хотим освободить эту память как можно скорее поэтому он доступен для новых входящих запросов.

Ответ 6

Все потоки реализуют IDisposable. Оберните свой поток памяти в операторе using, и вы будете в порядке и денди. Блок использования гарантирует, что ваш поток будет закрыт и расположен независимо от того, что.

где бы вы ни называли Foo, вы можете использовать (MemoryStream ms = foo()), и я думаю, вы все равно должны быть в порядке.

Ответ 7

У вас не будет утечки памяти, но ваш корректор кода правильно указал, что вы должны закрыть свой поток. Это вежливо для этого.

Единственная ситуация, в которой вы могли бы утечка памяти, - это когда вы случайно оставляете ссылку на поток и никогда не закрываете его. Вы по-прежнему не испытываете утечки памяти, но вы ненужно увеличиваете количество времени, которое, по вашему утверждению, используете.

Ответ 8

Я бы рекомендовал обернуть MemoryStream в bar() в инструкции using в основном для согласованности:

  • Теперь MemoryStream не освобождает память на .Dispose(), но возможно, что в какой-то момент в будущем он может или вы (или кто-то другой в вашей компании) может заменить его собственным пользовательским MemoryStream, который делает это, и др.
  • Это помогает установить шаблон в вашем проекте для обеспечения того, чтобы все потоки были удалены - линия более жестко нарисована, говоря "все потоки должны быть удалены" вместо "некоторые потоки должны быть удалены, но некоторые из них не должны"...
  • Если вы когда-либо меняете код, чтобы позволить возвращать другие типы потоков, вам все равно придется его менять.

Еще одна вещь, которую я обычно делаю в таких случаях, как foo() при создании и возврате IDisposable, заключается в том, чтобы исключить любую ошибку при построении объекта и return за исключением исключения,

MemoryStream x = new MemoryStream();
try
{
    // ... other code goes here ...
    return x;
}
catch
{
    // "other code" failed, dispose the stream before throwing out the Exception
    x.Dispose();
    throw;
}

Ответ 9

Если объект реализует IDisposable, вы должны вызвать метод .Dispose, когда закончите.

В некоторых объектах Dispose означает то же, что и Close, и наоборот, в этом случае либо хорошо.

Теперь, для вашего конкретного вопроса, нет, вы не будете утечка памяти.

Ответ 10

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

Ответ 11

Устранение неуправляемых ресурсов не является детерминированным в сборках мусора. Даже если вы явно вызываете Dispose, вы абсолютно не контролируете, когда резервная память фактически освобождается. Dispose неявно вызывается, когда объект выходит за пределы области видимости, независимо от того, выходит ли она из оператора using или вызывается столбец из подчиненного метода. При этом все говорят, что иногда объект может быть оберткой для управляемого ресурса (например, файла). Вот почему хорошая практика явно закрывать в заявлениях finally или использовать инструкцию using. Приветствия

Ответ 12

MemorySteram - это не что иное, как массив байтов, который является управляемым объектом. Забудьте об утилизации или закрытии, это не имеет побочного эффекта, кроме как над заголовком завершения.
Просто проверьте конструктор или флеш-метод MemoryStream в рефлекторе, и будет ясно, почему вам не нужно беспокоиться о закрытии или удалении его, а не просто о соблюдении надлежащей практики.