2016-12-03 3 views
0

У меня проблема с моим приложением, надеюсь, что кто-то может дать мне советы по его исправлению.Высокое использование ЦП после долгого времени

У меня многопоточное приложение. Он настраивает 10-20 потоков, и в каждом потоке я выполняю некоторые сложные задачи.

Thread thread = new Thread(ProcessThread); 
thread.Start(); 

private void ProcessThread() 
{ 
    while(IsRunning) 
    { 
     // do some very complex operations: grab HTTP pages. Save to files. Read from files. Run another threads etc. 
    } 
} 

В начале приложение использует около 10% процессора и 140 Мб памяти. Но после 1000 часов использование процессора составляет 25% -30%, а память - 1200 МБ. Я знаю, что, возможно, у меня есть утечка памяти в моем коде, я постараюсь это исправить. Но что случилось с процессором? Почему он растет? Каждое выполнение выполняет те же операции, что и в начале и позже (например, открывать веб-страницу, захватывать некоторую информацию и сохранять ее в файле).

Я думаю, что проблема может быть с GC. Больше приложений памяти, больше процессора нужно очистить память?

Другой вопрос, не могли бы вы посоветовать хороший инструмент, как измерить, что такое процессор в моем приложении?

А может быть, вы можете порекомендовать хороший инструмент для анализа памяти и проверки того, где она протекает? Я пробовал JetBrains dotMemory, но не очень понял. Возможно ты можешь помочь мне. Стаж: http://prntscr.com/dev067 http://prntscr.com/dev7a2 Как я вижу, у меня действительно не слишком много неуправляемой памяти. Но в то же время я вижу проблемы со строками, но не могу понять, что не так, поскольку GC должен его очистить?

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

ответ

0

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

GC не будет собирать все мертвые предметы в цикле. Он будет использовать поколений. Это означает, что он будет собирать старые объекты только в том случае, если ему нужно пространство. Таким образом, использование памяти увеличится до первой коллекции. Затем собирается только небольшая часть элементов, что несколько снизит использование памяти, но не полностью. И GC также не обязательно возвращает освобожденную память ОС.

В более высоких версиях Visual Studio вы найдете профайлер памяти.

0

Очень неясно, что такое «некоторые сложные задачи». И это особенно меня беспокоит:

// do some very complex operations: grab HTTP pages. Save to files. Read from files. Run another threads etc. 

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

То же самое касается использования памяти. Больше потоков = больше памяти требуется для каждого потока (у каждого потока есть собственный стек, требующий дополнительной памяти) ...

Кроме того, я не считаю, что GC является преступником здесь.

Есть много вопросов, на которые нужно ответить, прежде чем я смогу помочь вам найти причину такого поведения, и прежде чем мы сможем обвинить GC :): 1) Вы начинаете все, скажем, 20 потоков в начале вашей программы? 2) Создаете ли вы новые потоки из уже запущенных? 3) Что такое условие завершения для этих потоков?Действительно ли они заканчиваются?

Я бы рекомендовал использовать dotTrace для определения использования ЦП. К сожалению, я не использовал инструмент для профилирования памяти, поэтому я не могу рекомендовать его.

0

Я посмотрел на ваш скриншот, и из этого я вижу, что у вас есть много объектов, оставшихся поколением поколения 0, поэтому они перерастают в поколение 1, а затем в поколение 2. Это может быть признаком утечки памяти, но не обязательно , Не видя своего кода, трудно сказать. Все, что я могу сказать, это то, что вы сохраняете свои объекты в течение очень долгого времени. Это может потребоваться, но снова я не могу сказать, не видя кода.

Немного о GC

Когда GC просыпается очистки, он анализирует управляемую кучу, чтобы увидеть, какие объекты не укорененные и помечает их все готово к коллекции. Это называется отметкой . Затем он начинает освобождать память. Это называется фазой . Если он ничего не может очистить, эти объекты переходят к поколению 1. Спустя некоторое время GC снова просыпается и повторяет выше, но на этот раз, поскольку в генерации 1 есть элементы, если они не могут быть собраны, они перейдут в поколение 2. Это может быть плохим знаком. Должны ли объекты надолго существовать?

Так что вы можете сделать?

Вы говорите, что GC должен чистить вещи. Ну да, GC будет чистить вещи, но только если вы не ссылаетесь на них. Если объект укоренен, GC не сможет его очистить.

Написать код с GC в виду

Для начала исследования, необходимо изучить код и убедитесь, что ваш код написан с GC в виду. Пройдите свой код и составите список всех объектов, которые вы используете. Если какой-либо объект класса, который реализует IDisposable затем обернуть их в использовании заявления:

using (Font font1 = new Font("Arial", 10.0f)) 
{ 
    // work here depends on font1 
} 

Если вам нужно font1 в одном из ваших классов как переменный уровня класса, то вы должны сделать descision когда звонить Dispose на нем: по крайней мере этот класс должен реализовать IDisposable и вызывать dispose на font1. Пользователи этого класса (любой код, который вызывает новый в этом классе) должны либо использовать этот класс с оператором using, либо называть его «Dispose».

Не храните объекты дольше, чем они вам нужны.

Еще нужно провести расследование?

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

У некоторых людей есть неправильное представление о том, что вызов GC.Collect() решит проблему утечки памяти. Это совсем не так. Принуждение сбора мусора будет по-прежнему следовать тем же правилам, и если объекты коренится, вы можете называть GC.Collect() неопределенно долго, объекты все равно не будут очищены.

Вот пример приложения, которое будет отображаться следующим образом:

public class Writer : IDisposable 
{ 
    public void Dispose() 
    { 

    } 

    public void Write(string s) 
    { 
     Console.WriteLine(s); 
    } 
} 

class Program 
{ 

    static void Main(string[] args) 
    { 
     Writer writer = new Writer(); 
     writer.Write("1"); 
     writer.Dispose(); 

     // writer will still be around because I am referencing it (rooted) 
     writer.Write("2"); 

     GC.Collect(); 

     // calling GC.Collect() has no impact since writer is still rooted 
     writer.Write("3"); 
     Console.ReadKey(); 

    } 
} 
0

Я могу только прокомментировать, почему у Вас есть вопрос CPU и не GC.

  1. Я предлагаю вам убедиться, что вы создаете только числа, которые вы ожидаете. Если у вас есть потоки, запускающие потоки, их легко проскальзывать.
  2. Как уже упоминалось в другом комментарии, создание и разрушение резьбы является дорогостоящим. Если вы постоянно создаете и уничтожаете потоки, это будет иметь заметное влияние на вашу производительность.
  3. Я подозреваю, что причиной замедления является перерыв памяти, что означает, что объем памяти, используемый каждым потоком, достаточно велик, и/или общая сумма, используемая всеми потоками, достаточно велика, чтобы заставить страницы памяти меняться и выходить все время. В некоторых ситуациях процессор может тратить больше времени на обмен памяти на диск и с диска, чем на выполнение потока.

Вот мои предложения:

  1. Использование пула потоков. Это минимизирует время, необходимое для создания и уничтожения потоков. Это также свяжет количество потоков, которые вы используете.
  2. Убедитесь, что каждый поток выполняет разумное количество времени перед его заменой - т. Е. У вас нет конфликта потоков.
  3. Убедитесь, что объем памяти, используемой каждой нитью, не намного больше, чем вы ожидаете, чтобы предотвратить переполнение памяти.
  4. Если вам нужно использовать большой объем памяти для каждого потока, убедитесь, что ваш шаблон использования таков, что много страниц подкачки не существует.
0

Запустите приложение с помощью "Start collecting allocations data immediately" enabled, получите моментальный снимок через некоторое время и посмотрите at memory traffic view. Чтобы понять, что занимает память, "All objects" grouped by type и посмотреть, какие типы занимают больше всего объема памяти.

Для профилирования производительности я мог бы порекомендовать JetBrains dotTrace (режим временной шкалы, для вашего случая).

Смежные вопросы