none
List, потоки. Съедает память RRS feed

  • Вопрос

  • Всем привет. Написал приложение. Спасибо всем тут, кто помогал советами. Мое приложение работает в локальной сети. Посылает запросы серверам на проверку message queue, заносит данные в список (List). Все это происходит в потоке. А поток (backgroundworker) я создаю динамически по таймеру. Каждые 3 секунды. Программа работает. Показывает результаты, обновляет. За 12 часов работы увидел проблему с памятью. Диспетчер задач при запуске показал примерно 20 000 КБ, пройдя 12 часов значение увеличилась на 170 000 КБ.

    Вот часть кода, в основном это работает:

    ConcurrentBag<MyFileInformation> list = new ConcurrentBag<MyFileInformation>();
    
    private void timer1_Tick(object sender, EventArgs e)
            {
                try
                {
    
                    
    
                    ThreadPool.QueueUserWorkItem(new WaitCallback((s) =>
    
                        backgroundWorker1_DoWork()
    
                        ));
    
                    
                }
                catch
                {
    
                }
            }
    
    void backgroundWorker1_DoWork(/*object sender, DoWorkEventArgs e*/)
            {
                try
                {
                    ConnectionOptions connection = new ConnectionOptions();
                    connection.Username = ConfigurationManager.AppSettings["username"].ToString();
                    connection.Password = ConfigurationManager.AppSettings["password"].ToString();
                    connection.Authority = "ntlmdomain:" + ConfigurationManager.AppSettings["domain"].ToString();
    
                    
    
    
                    for (int i = 0; i < treeView_machines.Nodes.Count; i++)
                    {
                        TreeNodeCollection node = treeView_machines.Nodes[i].Nodes;
    
                        
                        try
                        {
                            //treeView_machines.Invoke((MethodInvoker)delegate { treeView_machines.Nodes[i].ImageIndex = 0; });
                            for (int j = 0; j < node.Count; j++)
                            {
                                //if (node[j].Checked)
                                {
                                    string con = String.Format("\\\\{0}\\root\\CIMV2", treeView_machines.Nodes[i].Text);
    
                                    ManagementScope manage = new ManagementScope(con, connection);
    
                                    manage.Connect();
    
                                    ObjectQuery query = new ObjectQuery("SELECT * FROM Win32_PerfFormattedData_msmq_MSMQQueue");
    
                                    
                                    using (ManagementObjectSearcher searcher =
                                        new ManagementObjectSearcher(manage, query))
    
                                    {
                                        using (var managementObjectCollection = searcher.Get())
                                        {
                                            foreach (ManagementObject queryObj in managementObjectCollection)
                                            {
    
    
                                                if (node[j].Text == queryObj["Name"].ToString())
                                                {
                                                    int countmessage = Int32.Parse(queryObj["MessagesinQueue"].ToString());
                                                    list.Add(new MyFileInformation(treeView_machines.Nodes[i].Text, node[j].Text, countmessage));
                                                }
    
    
    
                                            }
                                        }
                                    }
                                    
    
                                }
    
                            }
                        }
                        
                        catch (Exception ex)
                        {
                            File.AppendAllText("MY_PROG_NAME_log.txt", DateTime.Now + Environment.NewLine + ex);
                            //treeView_machines.Invoke((MethodInvoker)delegate { treeView_machines.Nodes[i].ImageIndex = 10; });
                        }
                    }
    
                   
                }
                catch (Exception ex)
                {
                    File.AppendAllText("MY_PROG_NAME_log.txt", DateTime.Now + Environment.NewLine + ex);
                }
                
    
                
            }


    В основном на базе этого вся программа. Сканируются все сервера, сбор информации, заполняется список. Потом другой метод, показывает последние данные пользователю (с помощью массива). Таймер запуска потоков запускается каждые 2 секунды. Список очищается так:

    MyFileInformation t;
                    while (!list.IsEmpty)
                    {
                        list.TryTake(out t);
                        File.AppendAllText("MY_PROG_NAME_log.txt", DateTime.Now + Environment.NewLine + "TryTake");
                    }

    С помощью советов довел свой код до такого. Создал пул потоков, безопасную работу со списком, using и т.д.

    Но без помощи, уже не знаю че делать.

    По совету создал думп файл, но чтоб с ним работать нужен визуал 2013-я версия

    16 марта 2014 г. 19:33

Ответы

  • Используйте профилирование или расширение отладчика SOS. Вот пример тут и тут.

    Сделаем содержимое сообщества лучше, вместе!

    • Помечено в качестве ответа Frech871 17 марта 2014 г. 10:01
    16 марта 2014 г. 19:59
    Модератор
  • Да, в первую очередь используйте профайлер, как сказал Yatajga.

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

    Когда в системе свободной памяти много, рантайм её тратит и не спешит отдавать. Потому что освобождение памяти требует времени. А если она никому больше не нужна - зачем тратить время на её освобождение? Вся она будет возвращена, когда приложение завершит свою работу.

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

    Можете создать простенькую программку (консольную), которая постепенно потребляет память (List.Add в цикле) и запустить её параллельно с вашим приложением. Если ваше приложение освободит занятую память (смотрите в Диспетчере задач), когда программка выжрет всю свободную, значит всё нормально - рантайм не тратил время на её освобождение заранее.

    -----

    Совет по увеличению надёжности.

    Метод File.AppendAllText может выбрасывать исключения. Например, IOException может произойти в любой момент (жёсткие диски часто сбоят). Поэтому желательны обработчики и этих случаев.

    -----

    И переименуйте метод backgroundWorker1_DoWork - дайте ему более говорящее название. Ведь BackgroundWorker уже не используется?

    • Помечено в качестве ответа Frech871 17 марта 2014 г. 10:01
    17 марта 2014 г. 9:10

Все ответы

  • Используйте профилирование или расширение отладчика SOS. Вот пример тут и тут.

    Сделаем содержимое сообщества лучше, вместе!

    • Помечено в качестве ответа Frech871 17 марта 2014 г. 10:01
    16 марта 2014 г. 19:59
    Модератор
  • Да, в первую очередь используйте профайлер, как сказал Yatajga.

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

    Когда в системе свободной памяти много, рантайм её тратит и не спешит отдавать. Потому что освобождение памяти требует времени. А если она никому больше не нужна - зачем тратить время на её освобождение? Вся она будет возвращена, когда приложение завершит свою работу.

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

    Можете создать простенькую программку (консольную), которая постепенно потребляет память (List.Add в цикле) и запустить её параллельно с вашим приложением. Если ваше приложение освободит занятую память (смотрите в Диспетчере задач), когда программка выжрет всю свободную, значит всё нормально - рантайм не тратил время на её освобождение заранее.

    -----

    Совет по увеличению надёжности.

    Метод File.AppendAllText может выбрасывать исключения. Например, IOException может произойти в любой момент (жёсткие диски часто сбоят). Поэтому желательны обработчики и этих случаев.

    -----

    И переименуйте метод backgroundWorker1_DoWork - дайте ему более говорящее название. Ведь BackgroundWorker уже не используется?

    • Помечено в качестве ответа Frech871 17 марта 2014 г. 10:01
    17 марта 2014 г. 9:10
  • спасибо большое, проверю. Шас возможности нет, так как дома. А локальная сеть на работе, там запустить надо
    17 марта 2014 г. 10:05
  • хочу подметить одно, даже если запустить программу на машине, у которой нет выхода в сеть, добавить сервера, которые не доступны, тогда тоже увеличивается память. Дела не в списке (list), так как он не заполняется данными. 
    17 марта 2014 г. 10:11
  • Да, в первую очередь используйте профайлер, как сказал Yatajga.

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

    Когда в системе свободной памяти много, рантайм её тратит и не спешит отдавать. Потому что освобождение памяти требует времени. А если она никому больше не нужна - зачем тратить время на её освобождение? Вся она будет возвращена, когда приложение завершит свою работу.

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

    Можете создать простенькую программку (консольную), которая постепенно потребляет память (List.Add в цикле) и запустить её параллельно с вашим приложением. Если ваше приложение освободит занятую память (смотрите в Диспетчере задач), когда программка выжрет всю свободную, значит всё нормально - рантайм не тратил время на её освобождение заранее.

    -----

    Совет по увеличению надёжности.

    Метод File.AppendAllText может выбрасывать исключения. Например, IOException может произойти в любой момент (жёсткие диски часто сбоят). Поэтому желательны обработчики и этих случаев.

    -----

    И переименуйте метод backgroundWorker1_DoWork - дайте ему более говорящее название. Ведь BackgroundWorker уже не используется?

    File.AppendAllText  захват ошибок и для него делать? Как ? В ветке catch ?
    17 марта 2014 г. 11:59
  • скрины анализа думп файла

    17 марта 2014 г. 16:53
  • После тестирование решил тут написать про одну проблему: Приложение в диспетчере задач много места не держит. Показывает что поднимается до 150 М, потом снижается до 80 М. Очищается. Я после определенного времени список очищаю. Но в Течении недели заметил одно, оперативка растет. Когдаприложение запускал, показывал 1ГБ, шас уже 3 ГБ используется. Неужели мусор от приложение? Не управляемая память. Если закрыть приложение, то память вернется в свой предел, 1ГБ. Вот как с этим боротся?
    29 марта 2014 г. 11:42
  • Я же вам предложил использовать SOS. Когда приложение будет съедать много памяти, цепляйтесь к нему отладчиком WinDbg и смотрите состояние памяти.

    Сделаем содержимое сообщества лучше, вместе!

    29 марта 2014 г. 11:47
    Модератор
  • не смог понять как им пользоваться. ПРиложение запускаю на компе клиента, там нет возможности установить визуалы и т.д. Объясните если что
    29 марта 2014 г. 11:52
  • Как раз отладчик Windows весит совсем немного, поэтому я его и предложил.

    Сделаем содержимое сообщества лучше, вместе!

    29 марта 2014 г. 11:56
    Модератор
  • можете послать ссылку про windbg ?Буду благодарен.

    А то, че та не так делаю. Какие та длл не находит

    29 марта 2014 г. 11:59
  • Вот тут я описал как его устанавливать и откуда качать. Скорее всего у вас символы отладки не загружаются, посмотрите тут как правильно их настроить. Ну и если не сможете настроить отладчик Windows, то можете воспользоваться сторонним профайлером, например dotTrace. Он прлатный, но в течении 10 дней вы можете использовать его.

    Сделаем содержимое сообщества лучше, вместе!

    29 марта 2014 г. 12:24
    Модератор