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


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

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

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

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

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

12 100

12 ответов:

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

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

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

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

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

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

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

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

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

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

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

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

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

to:

Stream foo()
{    
   ...
}

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

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

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

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

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

вызов .Dispose() (или обертывание с Using) не требуется.

причина позвонить .Dispose() Это освободить ресурс как можно скорее.

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

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

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

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

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

еще одна вещь, которую я обычно делаю в таких случаях, как 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 означает то же самое, что и Close и наоборот, в этом случае либо хорошо.

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

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

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

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