Проблемы управления памятью в C # StreamWriter и StreamReader, почему память не используется для освобождения памяти? - PullRequest
3 голосов
/ 22 июня 2010

Так что я использую StreamReader, который использует MemoryStream для записи в StreamWriter и внутри этого приложения, но использование памяти увеличивается на 300 МБ (из одного из более крупных входов) и не освобождается после того, как я закончу его использовать:

StreamWriter log = new StreamWriter("tempFile.txt");
log.Write(reader.ReadToEnd());
log.Close();

reader.DiscardBufferedData();
reader.Close();
reader.Dispose();
memoryStream.Dispose();
log.Dispose();
GC.Collect();

До этого и сразу после того, как я получаю использование ОЗУ, и до того, как оно будет на 300 МБ меньше, чем после, но я не знаю почему.Я сделал все, что мог придумать, чтобы освободить эту память, учитывая, что здесь происходит только то, что данные из считывателя помещаются в текстовый файл. Я не понимаю, зачем вообще понадобилось бы использовать какой-то большой объем памяти.временно.Что-то мне не хватает? ... Спасибо.

Ответы [ 4 ]

6 голосов
/ 22 июня 2010

Вы смотрите на оперативную память, используемую самим процессом? Я не ожидал бы, что это пойдет вниз. Процесс будет зависать от этой памяти и использовать ее для дальнейшего распределения. Он не передает его обратно в операционную систему. Так работает .NET.

Вы можете потенциально заставить его отбросить память с помощью некоторого вызова CLR API - но обычно я бы не стал.

Это не означает, что память действительно просочилась - она ​​все еще может использоваться тем же процессом. Например, если вы снова выполнили то же действие, вероятно, не потребуется увеличивать размер кучи - вы увидите, что использование памяти останется неизменным на уровне процесса. Используйте графы CLR perfmon для просмотра памяти, используемой в управляемой куче, чтобы определить, есть ли подлинная утечка.

(Существует также различная мера того, сколько памяти фактически использует приложение. Какая из них интересна, зависит от того, что вы пытаетесь сделать.)

РЕДАКТИРОВАТЬ: Если ваш фрагмент кода действительно указывает на код, который вы используете, то есть гораздо более простая альтернатива: поток данных.

using (TextWriter writer = File.CreateText("tempFile.txt"))
{
    CopyText(reader, writer);
}

static void CopyText(TextReader reader, TextWriter writer)
{
    char[] buffer = new char[8192];
    int charsRead;
    while ((charsRead = reader.Read(buffer, 0, buffer.Length)) > 0)
    {
        writer.Write(buffer, 0, charsRead);
    }
}

Обратите внимание, что если вы на самом деле не меняете кодировку, вы можете сделать это без использования пары TextWriter / TextReader для начала.

Таким образом, вам не нужно иметь всю строку в памяти , а также ее двоичное представление MemoryStream). Конечно, у вас все еще будет двоичное представление - у вас есть есть для записи всего в MemoryStream для начала?

2 голосов
/ 22 июня 2010

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

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

В любом случае, вы можете сделать следующее:

  • Вы можете попробовать запустить другое приложение, интенсивно использующее память, чтобы увидетьосвобождает ли среда выполнения память, когда система нуждается в этом
  • Вы можете использовать некоторые средства профилирования .NET, чтобы увидеть, есть ли какие-либо живые объекты, которые вы ожидаете собрать после запуска метода
0 голосов
/ 22 июня 2010

В упомянутом выше сегменте все log, reader и memoryStream все еще находятся в области видимости и поэтому не могут быть собраны сборщиком мусора.Я не знаю, каково значение Dispose для этих объектов, но вполне вероятно, что они по-прежнему хранят большую часть данных в памяти даже после вызова Dispose (поскольку Dispose обычно закрывает только файловые дескрипторы,неуправляемая память и тому подобное, и не обязательно удаляет внутренние буферы).Если вы хотите, чтобы это работало должным образом, вы должны позволить всем ссылочным объектам выйти из области видимости и перестать ссылаться.

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

0 голосов
/ 22 июня 2010

Я не уверен, поможет ли это, но попробуйте using scoping:

using (StreamReader reader = new StreamReader(somestream))
using (StreamWriter log = new StreamWriter("tempFile.txt"))
{
    log.Write(reader.ReadToEnd());
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...