Разве «не выбрасывать StreamWriter» может вызвать memoryLeak в этом случае? - PullRequest
3 голосов
/ 31 мая 2011

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

public int TranslateOOV(string word, Stream logStream)
{
StreamWriter writer = new StreamWriter(logStream);

//Do some logging
//dont close the writer and leave the caller close the stream
}

Я не закрываю StreamWriter, так как вызывающая сторона должна закрывать внутренний поток, это вызывает утечку памяти?

Ответы [ 4 ]

5 голосов
/ 31 мая 2011

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

protected override void Dispose(bool disposing)
{
    try
    {
        if (this.stream != null)
        {
            if (disposing || (this.Closable || this.stream as __ConsoleStream))
            {
                this.Flush(true, true);
                if (this.mdaHelper != null)
                {
                    GC.SuppressFinalize(this.mdaHelper);
                }
            }
        }
    }
    finally
    {
        if (this.Closable)
        {
            if (this.stream != null)
            {
                if (disposing)
                {
                    this.stream.Close();
                }
                this.stream = null;
                this.byteBuffer = null;
                this.charBuffer = null;
                this.encoding = null;
                this.encoder = null;
                this.charLen = 0;
                base.Dispose(disposing);
            }
        }
    }
}

Немного многословно, но я думаю, что это говорит нам о том, что удаление потока заботится о единственном доступном ресурсе, используемом StreamWriter. Поля byteBuffer и charBuffer являются массивами, encoding и encoder не являются одноразовыми, а база Dispose является виртуальной, поэтому Stream - единственное, что может вызвать проблемы, если он не очищен.

Я думаю, это также проясняет, что если вы хотите записать содержимое потока и впоследствии оставить его в пригодном для использования состоянии, то вы самым вызывающим образом не хотите избавиться от вашего StreamWriter, потому что это будет распоряжаться потоком (Закрыть звонки Dispose(true)). Вы также хотите удостовериться, что вы сбрасываете положение потока, так как вы, несомненно, измените его, прочитав содержимое. Разумеется, это также означает, что вы, вероятно, захотите проверить свойство CanSeek в Stream и убедиться, что после прочтения содержимого вы сможете вернуть положение, в котором оно было раньше.

0 голосов
/ 31 мая 2011

Это не моя сильная сторона, но да, вы должны поместить StreamWriter в блок Using (), поскольку утилита Streamwriter не должна утилизировать поток.Если он действительно удаляет поток, вы должны вернуть ссылку на Streamwriter, чтобы его можно было очистить дальше.

Редактировать: Отличное случайное кодирование в Microsoft.StreamWriter не будет распоряжаться потоком, как вы можете непосредственно увидеть в ответе @ckramer, он делает Close поток.Копаем в метод close http://msdn.microsoft.com/en-us/library/system.io.stream.close.aspx

Этот метод вызывает Dispose, указывая true для освобождения всех ресурсов.Вам не нужно специально вызывать метод Close.Вместо этого убедитесь, что каждый объект Stream правильно расположен.

Рад видеть, что Microsoft напрямую игнорирует свои собственные заявления.Это становится еще лучше.

Примечания для разработчиков

В производных классах не переопределяйте метод Close, вместо этого поместите всю логику очистки Stream в метод Dispose.

Это абсолютно бессмысленно.Таким образом, они не только игнорируют свои первые утверждения, но и создают класс, который нарушает принцип Open-Closed, давая вам возможность изменять класс таким образом, который может привести к непреднамеренным сценариям.Для такого утверждения, что Close не должен быть абсолютно виртуальным, или они должны были исправить все рассуждения, которые были приняты в этом рассмотрении.

0 голосов
/ 31 мая 2011

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

0 голосов
/ 31 мая 2011

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

...