Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > .NET Frameworks (точка нет фреймворки) > C# (си шарп)
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 22.12.2015, 15:00   #1
KiPiK
 
Регистрация: 31.12.2011
Сообщений: 5
По умолчанию Переполнение/утечка памяти программы - C#

Пишу программу для парсинга одного сайта. Сам сайт парсится с помощью CsQuery. Нужно за раз обработать нужный диапазон страниц сайта. Задаётся начальная и конечная ссылки для парсинга и программа в несколько потоков перебирает все страницы в диапазоне и извлекает нужную информацию в List, что бы после окончания сохранить всё в файл. Нужное количество потоков запускается, и они по очереди берут из счётчика текущей страницы свой номер и работают с ним. В потоках написан цикл While, что бы они не закрывались, пока не спарсили последнюю страницу. После окончания парсинга отдельно сохраняется вся информация в List. Но проблема в том, что парсится будут большие диапазоны страниц больше миллиона, а при тестовом запуске на диапазоне в 10 000 страниц программа начинает занимать в памяти больше 1,5 гигабайт. В отдельной программе пробовал заполнять List случайными данными, по типу тех, что должны были быть извлечены. Добавил 100 000 строк, и размер оперативной памяти, используемой программы не превышал 100 мегабайт. Парсинг так же работает правильно, никаких избыточных данных он не добавляет. Я грешу на мою неправильную работу с потоками, и то, что сборщик мусора не уничтожает данные с прошлых проходов парсинга. Пробовал разные способы так и не решил проблему с утечкой памяти. Помогите найти ошибку, или подсказать более правильный метод работы с потоками. Код прикладываю.
Код:
class Program
    {
        static int begin_of_post = 2950774;      //начальный индекс постов
        static int end_of_post = 2951774;        //конечный индекс
        static int current_post;                 //текущий пост для потоков
        
        static List<string> list_posts = new List<string>();   //список хранения данных о постах
 
 
        static void Main(string[] args)
        {
            ServicePointManager.DefaultConnectionLimit = 1000000000;   // количество одновременных соединений
 
            current_post = begin_of_post;
 
            Thread my_tr;                               
            for (int i = 0; i < 10; i++)            //запуск потоков
            {
                my_tr = new Thread(parse_site);
                my_tr.Start();
            }
 
            Console.ReadLine();
            save_to_file();
        }
 
        static void parse_site()
        {
            while (current_post <= end_of_post)
            {
                int link_to_post =current_post;                 //ссылка на пост
                Interlocked.Increment(ref current_post);        //инкремент счётчика
 
                CQ cq;
                try
                {
                    cq = CQ.CreateFromUrl("http://site.ru/" + link_to_post);        // загрузка кода страницы
                }
                catch
                {
                    Console.WriteLine("Error " + link_to_post);
                    continue;
                }
                
                string post_info;
                ...
                //сам парсинг сайта
                ...
                
                int current = int.Parse(link_to_post) - begin_of_post;          
                int end = end_of_post - begin_of_post;
                Console.WriteLine("Обработана ссылка " + current.ToString() + " ИЗ " + end.ToString());
                                                
                Thread my_tr_save=new Thread(save_post);
                my_tr_save.Start(post_info);
            }
        }
 
        
 
        static void save_post(object post_info)
        {
            ...
            // Парсинг информации о странице
            ...
            
            lock (list_posts)
            {
                list_posts.Add(post_info.ToString());
            }
        }      
 
        static void save_to_file()
        {
                        ...
            //сохранение строк list_posts в файл
                        ...
        }
    }
KiPiK вне форума Ответить с цитированием
Старый 22.12.2015, 15:48   #2
Пепел Феникса
Старожил
 
Аватар для Пепел Феникса
 
Регистрация: 28.01.2009
Сообщений: 21,000
По умолчанию

забавно что вы не ждете окончания ваших потоков.

у вас часом list_posts не огромный вырастает?
хранить то все в памяти.
Хорошо поставленный вопрос это уже половина ответа. | Каков вопрос, таков ответ.
Программа делает то что написал программист, а не то что он хотел.
Функции/утилиты ждут в параметрах то что им надо, а не то что вы хотите.
Пепел Феникса вне форума Ответить с цитированием
Старый 22.12.2015, 15:59   #3
KiPiK
 
Регистрация: 31.12.2011
Сообщений: 5
По умолчанию

Цитата:
забавно что вы не ждете окончания ваших потоков.
А зачем их ждать? Уменя отобраджается на консоли процесс выполнения, и после этого можно выполнять остальные действия.

Цитата:
у вас часом list_posts не огромный вырастает?
хранить то все в памяти.
Я же написал, что тестировал list_posts, пробовал его исключать из кода, это не вызывает никаких изменений,расход памяти всё равно тот же.
KiPiK вне форума Ответить с цитированием
Старый 22.12.2015, 16:38   #4
Пепел Феникса
Старожил
 
Аватар для Пепел Феникса
 
Регистрация: 28.01.2009
Сообщений: 21,000
По умолчанию

Код:
                my_tr_save.Start(post_info);
попробуйте заменить на

Код:
my_tr_save.Start(post_info);
GC.Collect();
но это неверно.

возможно ли ваш CQ переиспользовать?

скорее всего дело в том что в рабочем процессе сборка мусора происходит реже.
поидее она должна не допустить краша.
Хорошо поставленный вопрос это уже половина ответа. | Каков вопрос, таков ответ.
Программа делает то что написал программист, а не то что он хотел.
Функции/утилиты ждут в параметрах то что им надо, а не то что вы хотите.
Пепел Феникса вне форума Ответить с цитированием
Старый 22.12.2015, 17:01   #5
Luuzuk
Форумчанин
 
Аватар для Luuzuk
 
Регистрация: 18.01.2012
Сообщений: 975
По умолчанию

1) Сколько всего оперативной памяти на машине, на которой запускается программа?
2) Мешает ли потребление памяти корректной работе? (Программа падает?)
Благодарить в репутацию. Проклинать — туда же
Luuzuk вне форума Ответить с цитированием
Старый 22.12.2015, 17:06   #6
KiPiK
 
Регистрация: 31.12.2011
Сообщений: 5
По умолчанию

Цитата:
my_tr_save.Start(post_info);
GC.Collect();
Пробовал, знаю, что неправильно, но в целом не решает проблемы с занимаемой оперативной памятью. И вдобавок сильно сказывается на быстродействии потока.
KiPiK вне форума Ответить с цитированием
Старый 22.12.2015, 17:08   #7
KiPiK
 
Регистрация: 31.12.2011
Сообщений: 5
По умолчанию

Цитата:
1) Сколько всего оперативной памяти на машине, на которой запускается программа?
8 GB
Цитата:
2) Мешает ли потребление памяти корректной работе? (Программа падает?)
Нет программа не падает, но ей придётся обрабатывать диапозоны от миллиона адресов, а она уже при 10 тысячах занимает 1,5 GB. Сами понимаете никакой памяти не хватит.
KiPiK вне форума Ответить с цитированием
Старый 22.12.2015, 17:11   #8
Пепел Феникса
Старожил
 
Аватар для Пепел Феникса
 
Регистрация: 28.01.2009
Сообщений: 21,000
По умолчанию

Цитата:
Сообщение от KiPiK Посмотреть сообщение
8 GB

Нет программа не падает, но ей придётся обрабатывать диапозоны от миллиона адресов, а она уже при 10 тысячах занимает 1,5 GB. Сами понимаете никакой памяти не хватит.
сборщик мусора проверяет сколько памяти еще свободно и от этого может принять решение не проводить сборку сейчас. чтоб не страдало быстродействие как вы сами заметили.
посмотрите дальше, будет ли повышение или падение проги.
Хорошо поставленный вопрос это уже половина ответа. | Каков вопрос, таков ответ.
Программа делает то что написал программист, а не то что он хотел.
Функции/утилиты ждут в параметрах то что им надо, а не то что вы хотите.
Пепел Феникса вне форума Ответить с цитированием
Старый 22.12.2015, 17:13   #9
Luuzuk
Форумчанин
 
Аватар для Luuzuk
 
Регистрация: 18.01.2012
Сообщений: 975
По умолчанию

Цитата:
Сами понимаете никакой памяти не хватит.
Нет, это вы не понимаете как работает сборщик мусора. Когда количество свободной памяти будет поджимать, он сам запустится и почистит её. "А что же он раньше не запускается?" - а чтобы не замедлять исполнение программы, ибо свободной памяти еще хоть опой жуй)

Переписали бы вы это на Task Parallel Library (TPL), а то сердце кровью обливается от нерационального создания потоков. Ну и на производительности оно серьезно сказывается
Благодарить в репутацию. Проклинать — туда же
Luuzuk вне форума Ответить с цитированием
Старый 22.12.2015, 17:17   #10
KiPiK
 
Регистрация: 31.12.2011
Сообщений: 5
По умолчанию

Цитата:
Переписали бы вы это на Task Parallel Library (TPL)
А что именно использовать в TLP? Parralel.For не подходит для работы с сетью, создаётся мало потоков
KiPiK вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Утечка памяти Vlad2891 Общие вопросы Delphi 11 27.02.2015 16:06
утечка памяти Кудаив Помощь студентам 1 30.04.2012 18:18
Утечка памяти Juffin Общие вопросы Delphi 3 02.11.2010 12:11
Утечка памяти ZvEr_HaCkEr Свободное общение 13 24.09.2010 19:30
утечка памяти в С++ vengo Общие вопросы C/C++ 9 10.06.2008 21:24