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

StackOverflow https://stackoverflow.com/questions/234059

Вопрос

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

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

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

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

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

Это было полезно?

Решение

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

Это в конечном итоге будет очищено сборщиком мусора, но это всегда хорошая практика. Если вы запустите FxCop для своего кода, он пометит его как предупреждение.

Другие советы

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

Вызов Dispose не приведет к более быстрой очистке памяти, используемой MemoryStream. Он будет мешать вашему потоку быть жизнеспособным для вызовов чтения / записи после вызова, что может или не может быть полезно для вас.

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

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

Да, есть утечка , в зависимости от того, как вы определяете УТЕЧКУ, и сколько ПОЗЖЕ вы имеете в виду ...

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

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

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

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

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

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

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

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

Вызов .Dispose () (или перенос Использование ) не требуется.

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

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

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

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

Я бы рекомендовал обернуть 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;
}

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

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

Теперь, по вашему конкретному вопросу, нет, вы не потеряете память.

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top