Почему память не освобождается после записи файлов на диск в C # - PullRequest
0 голосов
/ 02 января 2012

У меня есть сложный алгоритм, который получает данные из сокетных соединений, преобразовывает данные и сохраняет их как можно скорее на HD. Данные, из-за этого я не хочу, чтобы обработка замедлялась, сохраняются используя другой поток. Алгоритм хранения данных напоминает эту структуру. По сути, это сохраняет XML на диске.

Begin Thread
 beginthread:

 XmlTextWriter xmltextWriter;
 Save Xml file 1
 xmltextWrite.close();

 XmlTextWriter xmltextWriter;
 Save Xml file 2
 xmltextWrite.close();

 goto beginthread:
End Thread

Это работает правильно, но если я загляну в Диспетчер задач, я могу заметить, что объем памяти, используемой моей программой, быстро увеличивается с течением времени (500 МБ после 1 часа работы). Это может быть оправдано из-за того, что поток не так быстр, как поступающие данные, и .NET Framework хранит для меня временное все в памяти. Но чего я не понял, так это того, что если входящие сокетные соединения прекратятся, даже через несколько минут, когда поток продолжит работать. Диспетчер задач продолжает показывать 500 МБ памяти. Почему память не передается ?! Объект XmlTextWriter является локальной переменной и каждый раз закрывается.

По запросу. Это часть кода

     beginthread:
        if (sleeptime < 1000) sleeptime += 2;

        try
        {

            while (hashBeginConn.Count > 0)
            {
                sleeptime = 0;

                int connToApply = hashBeginConn[0];

                if (olddate.ToShortDateString() != ListsockConnections[connToApply].beginDate.ToShortDateString())
                {
                    JoinDataFromTempFile(ListsockConnections[connToApply].beginDate.Date.Subtract(olddate.Date).Days, false, d);
                    olddate = ListsockConnections[connToApply].beginDate.Date;
                }

                if (tocreate)
                {
                    // XML Serialization
                    XmlTextWriter xmltextWriter;

                    Encoding enc = null;
                    if (ListsockConnections[connToApply].ENCfromCode) enc = Encoding.GetEncoding(ListsockConnections[connToApply].codepage);
                    if (ListsockConnections[connToApply].ENCDefault) enc = Encoding.Default;
                    if (ListsockConnections[connToApply].ENCfromText) enc = Encoding.GetEncoding(ListsockConnections[connToApply].codename);
                    if (enc == null) { enc = null; }

                    // xmltextWriter = new XmlTextWriter(folderPath + "\\" + cacheFileName, enc);
                    xmltextWriter = new XmlTextWriter(DataPath + "\\_temp.xml", enc);
                    xmltextWriter.Formatting = Formatting.Indented;

                    // Start document
                    // xmltextWriter.WriteStartDocument();
                    xmltextWriter.WriteStartElement("ConnectionList");
                    xmltextWriter.WriteStartElement("connection");

                    xmltextWriter.WriteStartElement("ConnectionCounter");
                    xmltextWriter.WriteValue(ListsockConnections[connToApply].ConnectionCounter.ToString());
                    xmltextWriter.WriteEndElement();

                    xmltextWriter.WriteStartElement("IDConnection");
                    xmltextWriter.WriteValue(ListsockConnections[connToApply].IDConnection.ToString());
                    xmltextWriter.WriteEndElement();

                    xmltextWriter.WriteStartElement("Parsed");
                    xmltextWriter.WriteValue("false");
                    xmltextWriter.WriteEndElement();

                    xmltextWriter.WriteStartElement("connType");
                    xmltextWriter.WriteValue("TCP/IP");
                    xmltextWriter.WriteEndElement();

                    xmltextWriter.WriteStartElement("beginConn");
                    xmltextWriter.WriteValue(ListsockConnections[connToApply].beginDate.ToString());
                    xmltextWriter.WriteEndElement();

                    xmltextWriter.WriteStartElement("remoteAddressFamily");
                    xmltextWriter.WriteValue(ListsockConnections[connToApply].remoteAdressFamily);
                    xmltextWriter.WriteEndElement();

                    xmltextWriter.WriteStartElement("remoteIP");
                    xmltextWriter.WriteValue(ListsockConnections[connToApply].remoteIP);
                    xmltextWriter.WriteEndElement();

                    xmltextWriter.WriteStartElement("localIP");
                    xmltextWriter.WriteValue(ListsockConnections[connToApply].localIP);
                    xmltextWriter.WriteEndElement();

                    xmltextWriter.WriteStartElement("remoteport");
                    xmltextWriter.WriteValue(ListsockConnections[connToApply].remoteport.ToString());
                    xmltextWriter.WriteEndElement();

                    xmltextWriter.WriteStartElement("localport");
                    xmltextWriter.WriteValue(ListsockConnections[connToApply].localport.ToString());
                    xmltextWriter.WriteEndElement();

                    xmltextWriter.WriteStartElement("dataEncoding");
                    if (ListsockConnections[connToApply].codepage != 0 || ListsockConnections[connToApply].codename != "")
                    {
                        if (ListsockConnections[0].codepage != 0)
                        { xmltextWriter.WriteValue(ListsockConnections[connToApply].codepage.ToString()); }
                        else
                        { xmltextWriter.WriteValue(ListsockConnections[connToApply].codename.ToString()); }
                    }
                    else
                    { xmltextWriter.WriteValue("NONE"); }
                    xmltextWriter.WriteEndElement();

                    xmltextWriter.WriteEndElement();
                    xmltextWriter.WriteEndElement();
                    xmltextWriter.Flush();
                    xmltextWriter.Close();

                    tocreate = false;
                }
                else
                {
                    FileInfo fi;
                    FileStream fstream;

                    //fi = new FileInfo(folderPath + "\\" + cacheFileName);
                    fi = new FileInfo(DataPath + "\\_temp.xml");
                    fstream = fi.OpenWrite();

                    XmlTextWriter xmltextWriter;

                    Encoding enc = null;
                    if (ListsockConnections[connToApply].ENCfromCode) enc = Encoding.GetEncoding(ListsockConnections[connToApply].codepage);
                    if (ListsockConnections[connToApply].ENCDefault) enc = Encoding.Default;
                    if (ListsockConnections[connToApply].ENCfromText) enc = Encoding.GetEncoding(ListsockConnections[connToApply].codename);
                    if (enc == null) { enc = null; }

                    xmltextWriter = new XmlTextWriter(fstream, enc);

                    xmltextWriter.Formatting = Formatting.Indented;

                    fstream.Position = fstream.Length - 17;

                    xmltextWriter.WriteRaw("  <connection>" + Environment.NewLine);

                    xmltextWriter.WriteRaw("     <ConnectionCounter>");
                    xmltextWriter.WriteValue(ListsockConnections[connToApply].ConnectionCounter.ToString());
                    xmltextWriter.WriteRaw("</ConnectionCounter>" + Environment.NewLine);

                    xmltextWriter.WriteRaw("     <IDConnection>");
                    xmltextWriter.WriteValue(ListsockConnections[connToApply].IDConnection.ToString());
                    xmltextWriter.WriteRaw("</IDConnection>" + Environment.NewLine);

                    xmltextWriter.WriteRaw("     <Parsed>");
                    xmltextWriter.WriteValue("false");
                    xmltextWriter.WriteRaw("</Parsed>" + Environment.NewLine);

                    xmltextWriter.WriteRaw("     <connType>");
                    xmltextWriter.WriteValue("TCP/IP");
                    xmltextWriter.WriteRaw("</connType>" + Environment.NewLine);

                    xmltextWriter.WriteRaw("     <beginConn>");
                    xmltextWriter.WriteValue(ListsockConnections[connToApply].beginDate.ToString());
                    xmltextWriter.WriteRaw("</beginConn>" + Environment.NewLine);

                    xmltextWriter.WriteRaw("     <remoteAddressFamily>");
                    xmltextWriter.WriteValue(ListsockConnections[connToApply].remoteAdressFamily);
                    xmltextWriter.WriteRaw("</remoteAddressFamily>" + Environment.NewLine);

                    xmltextWriter.WriteRaw("     <remoteIP>");
                    xmltextWriter.WriteValue(ListsockConnections[connToApply].remoteIP);
                    xmltextWriter.WriteRaw("</remoteIP>" + Environment.NewLine);

                    xmltextWriter.WriteRaw("     <localIP>");
                    xmltextWriter.WriteValue(ListsockConnections[connToApply].localIP);
                    xmltextWriter.WriteRaw("</localIP>" + Environment.NewLine);

                    xmltextWriter.WriteRaw("     <remotePort>");
                    xmltextWriter.WriteValue(ListsockConnections[connToApply].remoteport.ToString());
                    xmltextWriter.WriteRaw("</remotePort>" + Environment.NewLine);

                    xmltextWriter.WriteRaw("     <localport>");
                    xmltextWriter.WriteValue(ListsockConnections[connToApply].localport.ToString());
                    xmltextWriter.WriteRaw("</localport>" + Environment.NewLine);

                    xmltextWriter.WriteRaw("     <dataEncoding>");
                    if (ListsockConnections[connToApply].codepage != 0 || ListsockConnections[connToApply].codename != "")
                    {
                        if (ListsockConnections[connToApply].codepage != 0)
                        {
                            xmltextWriter.WriteValue(ListsockConnections[connToApply].codepage.ToString());
                        }
                        else
                        {
                            xmltextWriter.WriteValue(ListsockConnections[connToApply].codename.ToString());
                        }
                    }
                    else
                    {
                        xmltextWriter.WriteValue("NONE");
                    }
                    xmltextWriter.WriteRaw("</dataEncoding>" + Environment.NewLine);

                    xmltextWriter.WriteRaw("  </connection>" + Environment.NewLine);
                    xmltextWriter.WriteRaw("</ConnectionList>");

                    xmltextWriter.Flush();
                    xmltextWriter.Close();
                    fstream.Close();


                    if (fi.Length >= (maxFileTempSize * 1000000))
                    {
                        JoinDataFromTempFile(0, false, enc);
                    }
                }

                lock (lockThis)
                {
                    hashBeginConn.RemoveAt(0);
                }

            }

Ответы [ 3 ]

8 голосов
/ 02 января 2012

В ряде ответов говорится, что вы должны позвонить в Dispose.Хотя эти ответы значат хорошо, на самом деле они вам не помогут.Вы звоните Close, а Close и Dispose делают то же самое. Лучше практиковать использование блока «using», чтобы вы автоматически вызывали Dispose, но ваш код в порядке.

Реальный ответ на ваш вопрос:«Перестань беспокоиться об этом».Вы думаете об этом на неправильном уровне.Я предполагаю, что вы просматриваете либо «рабочий набор», либо «частные байты» в диспетчере задач, но вы можете не понимать, что это означает на самом деле.Большинство людей этого не делают.Этот ответ дает хорошее резюме:

Что такое частные байты, виртуальные байты, рабочий набор?

Хорошо, теперь, когда вы знаете, что такое "частные байты", этодолжно быть более понятно, почему это не проблема.Предположим, вы сборщик мусора в CLR.Вы выделяете кучу памяти от имени пользователя и используете ее для хранения управляемых объектов.Время от времени сборщик мусора работает, сжимая объекты в памяти и помечая память, ранее использовавшуюся ныне мертвыми объектами, как доступную. Но почему GC возвращает все эти блоки памяти операционной системе? GC имеет доказательства того, что вы относитесь к тому типу людей, которые пишут программы, использующие столько памяти, поэтому он хранит пустые страницы так,что вам не придется нести расходы на их повторное выделение позже, когда вы снова используете столько памяти через два миллисекунды.

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

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

5 голосов
/ 02 января 2012

Вы должны звонить xmltextWriter.Dispose().

Кроме того, память не будет освобождена до тех пор, пока не будет запущен сборщик мусора. Обычно вы должны позволять этому происходить автоматически, но вы можете явно вызывать его, используя статический метод GC.Collect (). НЕ рекомендуется явно вызывать gc.collect. Пусть CLR сделает это по собственному графику.

0 голосов
/ 02 января 2012

xmltextWriter содержит неуправляемых ресурсов, в данном случае файл, который является ресурсом операционной системы. Он не отпустит эти ресурсы, если вы явно не вызовете Dispose().

Лучше всего заключать использование писателя в блок using { }, чтобы Dispose() вызывался автоматически, даже если выдается исключение.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...