Поток C # не освобождает память

#c# #multithreading #memory-management

#c# #многопоточность #управление памятью

Вопрос:

У меня есть служба Windows, написанная на C#.Net . При запуске службы я создаю новый поток, как показано ниже

 new Thread(new ThreadStart(Function1)).Start();
  

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

 new Thread(new ThreadStart(Function2)).Start(); 
  

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

Моя проблема в том, что память, используемая вторым потоком, который считывает файл, не собирается. Я позволил своей службе работать в течение 3 часов, надеясь, что будет вызван GC, но ничего не произошло, и диспетчер задач по-прежнему показывает, что моя служба использует 150 МБ памяти. Функция для чтения и обработки текстового файла очень проста, и я уверен, что нет скрытых ссылок на массив строк, содержащий текст. Может ли кто-нибудь пролить свет на то, почему это происходит? Возможно ли, что поток, порожденный другим порожденным потоком, не может выполнить очистку после себя?

Спасибо

Комментарии:

1. Я думаю, это помогло бы реально увидеть код, который выполняется во 2-м потоке.

2. Правильно ли ваш второй поток удаляет поток, используемый для чтения файла?

3. Откуда вы знаете, что у вас утечка. Простое считывание чисел из диспетчера задач, как известно, подвержено ошибкам. Если вы на самом деле не знаете, как работает управление памятью Windows и управление памятью .net, вы неправильно истолкуете цифры.

4. Трудно понять проблему, но я могу предложить вам использовать ThreadPool вместо создания новых потоков вручную или использовать System. Многопоточность. Таймер — это обычная практика в WinServices для планирования некоторой фоновой работы.

5. «Использование 150 мегабайт памяти» в буквальном смысле бессмысленно . Это «память», которую вы используете в адресном пространстве? Если да, то какая ее часть является общей? Сколько из неразделяемого рабочего набора выделено, сколько зафиксировано и сколько находится в физической памяти вместо файла подкачки? Пока вы не знаете ответы на все эти вопросы, анализ использования памяти бессмыслен. Но, честно говоря, 150 мегабайт крошечные , и вам, вероятно, не стоит беспокоиться об этом. Можете ли вы объяснить, почему вас это волнует? Кроме того, не используйте диспетчер задач. Используйте профилировщик памяти для анализа использования памяти, вот для чего он нужен.

Ответ №1:

Доверьтесь сборщику мусора и перестаньте беспокоиться. 150 мегабайт — это ничто. При этом вы даже не измеряете размер файла; большая часть этого будет кодом.

Если вас беспокоит, куда уходит память, начните с понимания того, как работает память в современной операционной системе. Вам нужно понимать разницу между виртуальной и физической памятью, разницу между выделенной и выделенной памятью и все такое, прежде чем вы начнете разбрасываться цифрами вроде «150 мегабайт выделенной памяти». Помните, у вас есть 2000 мегабайт виртуального адресного пространства в 32-разрядном процессе; Я бы ни в коем случае не подумал, что процесс объемом 150 мегабайт является большим.

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

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

Комментарии:

1. Спасибо. Микроскоп, а не телескоп, заставил меня улыбнуться

2. Вероятно, лучшим инструментом для начала является Process Explorer ( technet.microsoft.com/en-us/sysinternals/bb896653 ). В нем содержится куча информации, специфичной для .NET

3. Process Explorer довольно полезен, но я бы предпочел начать с профилировщика CLR (скачать 1.1 , 2.0 , 4.0 )

Ответ №2:

Если вы используете диспетчер задач Windows, чтобы попытаться рассчитать используемую память, это, скорее всего, вводит вас в заблуждение. Память, используемая CLR, обычно не возвращается операционной системе, насколько мне известно… таким образом, вы потенциально все еще увидите высокий рабочий набор, даже несмотря на то, что большая часть этой памяти все еще доступна для повторного использования в процессе.

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

Комментарии:

1. Вероятно, это и есть решение. Есть ли какой-либо способ принудительно вернуть память операционной системе или это произойдет автоматически, если другому приложению потребуется эта память?

2. @newidforu: В операционной системе достаточно виртуальной памяти … хотя я полагаю, что GC может реагировать на глобальное давление. Признаюсь, я не совсем понимаю, точно , что происходит с точки зрения памяти. В идеале вы не хотите, чтобы «потраченная впустую» память заменялась, но в конечном итоге это может произойти…