2010-07-15 2 views
9

Я запускаю сервер Perl с 10 потоками. Они никогда не уничтожаются, пока программа не выйдет, но это то, что я намереваюсь иметь как можно больше времени, поэтому вот почему это проблема для меня. Нитки обрабатывают простую задачу много раз. Когда я запускаю сервер, и все потоки запускаются, я вижу, что у меня 288,30 МБ бесплатно. После нескольких итераций через каждый поток он сообщает 285,96 МБ бесплатно. Это не так уж плохо. Может быть, это просто какое-то пространство стека, выделенное или что-то во время этих итераций. Но через 15 минут бесплатная память опускается до 248,24 МБ! Что случилось с моей памятью? Теперь, интересно, это плато. Он продолжает медленно потреблять, но не так быстро, как вначале. Я подумал, что, возможно, это была моя ошибка, поэтому я попытался дважды проверить объем всех моих переменных и даже не определить их всех в конце цикла потока.Перл потоков медленно потребляет память

Я распечатываю свободное пространство после КАЖДОЙ итерации нитей, поэтому я могу наблюдать за ней медленно. Теперь интересно также, что он не уменьшается каждый раз. Иногда свободная память остается неизменной после итерации.

Я использую Perl 5.8.8, построенный из источника на Linux 2.6

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

ОБНОВЛЕНИЕ: Может ли это быть проблемой размера стека? Могу ли я выделить больше памяти для стека, чем мне нужно. Когда я создаю свои потоки, я не изменяю настройку по умолчанию. Нужно ли мне? В документе docs указано, что значение по умолчанию обычно составляет 16 МБ в зависимости от системы. 16x10 threads = 160MB -> который может быть виновником. Мысли?

ОБНОВЛЕНИЕ: Я построил и установил Perl 5.12.1 и перестроил модули и все такое. Запустил скрипт около часа, и вот что я заметил. Использование памяти теперь управляемо, но не идеально.

  • В начале сразу после нереста мои нити казались немного ниже. Вниз от ~ 60-66 МБ, выделенных для моих 10 потоков до ~ 45-50 МБ.
  • После нескольких итераций их использование увеличилось на 3 МБ (примерно такое же, как и раньше).
  • До этого момента я ожидал. Все это память заранее для нереста, а затем немного для переменных, которые я использую в своих потоках. Это та часть, которая мне не нравится. После работы около 10 минут, я теряю дополнительно 65 МБ! Почему он это делает? Если он уже повторил несколько раз штраф всего за 3 МБ, зачем продолжать выделять?
  • На данный момент он работает полтора часа, и они больше не используют дополнительный 65 МБ, это еще 84 МБ!
  • Это медленно занимает больше памяти, но странно, что количество свободной памяти не уменьшает каждую итерацию. Я распечатываю свободную память до и после каждой итерации, и она будет оставаться той же на некоторое время или навешивать + - вокруг определенного числа на некоторое время, а затем внезапно меняться на 5-10 МБ. Я не могу оставить эту работу более чем на один, два дня, потому что она начинает приближаться к 80/90% моей доступной памяти.

Есть ли другие идеи? Можно вообще попробовать? Я уже отказываюсь от всех переменных.

UPDATE: Я действительно хочу, чтобы перекомпилировать Perl с glibc в качестве последнего средства, так как я нашел несколько отчетов о том, что на некоторых вариантах Linux это будет segfault.Так как я последний раз опубликовал, я еще больше изучил возможность циклов в своих хэшах. Не нашел ничего. Поэтому я провел последние несколько дней, анализируя свою подпрограмму и кэшируя все, что используется на другой итерации. Многие новые вещи воссоздаются каждый раз, и Perl не очищает все это, даже если я явно отказываюсь от всего этого. Поэтому, если он не будет сотрудничать, я просто не уничтожу его. Посмотрите, поможет ли кеширование моих объектов. Позже опубликует статистику использования памяти.

ОБНОВЛЕНИЕ: Хм, очень странно. Даже после кэширования моих данных, которые будут использоваться повторно, память растет примерно с той же скоростью. Теперь он начинается выше, потому что я кеширую, но затем продолжает расти, хотя в основном я использую свои кешированные объекты. Это озадачивает. Угадайте, что пришло время попробовать glibc ... или это просто недостаток выбора Perl, и вам придется жить с перезагрузкой сервера каждые пару дней.

ОБНОВЛЕНИЕ: Пробовал без кеширования, без glibc, снова. Хорошо работает некоторое время, через несколько часов, затем он начинает расти. Просто хотел, чтобы вы увидели график.
http://tinypic.com/r/311nc08/3
http://i32.tinypic.com/311nc08.jpg

UPDATE: Вот выдержка из журнала регистрирующего свободной памяти до и после каждого потока в течение приблизительно минуты. Может быть, это поможет кому-то лучше понять проблему. Кажется, он немного стабилен, и тогда каждый так часто будет потреблять больше памяти. Здесь я теряю почти 40 МБ!

[9:8:30, Fri Jul 23, 2010] [0] Memory usage at end thread 1: 253.812736MB (obj cache: 136) 
[9:8:30, Fri Jul 23, 2010] [0] Memory usage at idle thread 1: 253.812736MB (obj cache: 136) 
[9:8:34, Fri Jul 23, 2010] [204] Sending data to thread 
[9:8:34, Fri Jul 23, 2010] [0] 3 - Creating a new obj 
[9:8:34, Fri Jul 23, 2010] [206] Sending data to thread 
[9:8:34, Fri Jul 23, 2010] [0] 4 - Creating a new obj 
[9:8:35, Fri Jul 23, 2010] [0] Memory usage at end thread 3: 253.812736MB (obj cache: 136) 
[9:8:35, Fri Jul 23, 2010] [0] Memory usage at idle thread 3: 253.812736MB (obj cache: 136) 
[9:8:35, Fri Jul 23, 2010] [0] Memory usage at end thread 4: 253.812736MB (obj cache: 136) 
[9:8:35, Fri Jul 23, 2010] [0] Memory usage at idle thread 4: 253.812736MB (obj cache: 136) 
[9:8:41, Fri Jul 23, 2010] [225] Sending data to thread 
[9:8:41, Fri Jul 23, 2010] [0] 2 - Creating a new obj 
[9:8:42, Fri Jul 23, 2010] [0] Memory usage at end thread 2: 253.681664MB (obj cache: 136) 
[9:8:42, Fri Jul 23, 2010] [0] Memory usage at idle thread 2: 253.681664MB (obj cache: 136) 
[9:8:47, Fri Jul 23, 2010] [243] Sending data to thread 
[9:8:47, Fri Jul 23, 2010] [0] 1 - Creating a new obj 
[9:8:48, Fri Jul 23, 2010] [0] Memory usage at end thread 1: 253.935616MB (obj cache: 136) 
[9:8:48, Fri Jul 23, 2010] [0] Memory usage at idle thread 1: 253.935616MB (obj cache: 136) 
[9:9:1, Fri Jul 23, 2010] [277] Sending data to thread 
[9:9:1, Fri Jul 23, 2010] [0] 3 - Creating a new obj 
[9:9:2, Fri Jul 23, 2010] [280] Sending data to thread 
[9:9:2, Fri Jul 23, 2010] [0] 4 - Creating a new obj 
[9:9:2, Fri Jul 23, 2010] [0] Memory usage at end thread 3: 253.935616MB (obj cache: 136) 
[9:9:2, Fri Jul 23, 2010] [0] Memory usage at idle thread 3: 253.935616MB (obj cache: 136) 
[9:9:3, Fri Jul 23, 2010] [283] Sending data to thread 
[9:9:3, Fri Jul 23, 2010] [0] 2 - Creating a new obj 
[9:9:4, Fri Jul 23, 2010] [284] Sending data to thread 
[9:9:4, Fri Jul 23, 2010] [0] 1 - Creating a new obj 
[9:9:4, Fri Jul 23, 2010] [0] Memory usage at end thread 2: 253.935616MB (obj cache: 136) 
[9:9:4, Fri Jul 23, 2010] [0] Memory usage at idle thread 2: 253.935616MB (obj cache: 136) 
[9:9:5, Fri Jul 23, 2010] [287] Sending data to thread 
[9:9:5, Fri Jul 23, 2010] [0] 3 - Creating a new obj 
[9:9:5, Fri Jul 23, 2010] [0] Memory usage at end thread 4: 253.93152MB (obj cache: 136) 
[9:9:5, Fri Jul 23, 2010] [0] Memory usage at idle thread 4: 253.93152MB (obj cache: 136) 
[9:9:6, Fri Jul 23, 2010] [290] Sending data to thread 
[9:9:6, Fri Jul 23, 2010] [0] 2 - Creating a new obj 
[9:9:7, Fri Jul 23, 2010] [0] Memory usage at end thread 3: 253.804544MB (obj cache: 136) 
[9:9:7, Fri Jul 23, 2010] [0] Memory usage at idle thread 3: 253.804544MB (obj cache: 136) 
[9:9:7, Fri Jul 23, 2010] [0] Memory usage at end thread 1: 253.804544MB (obj cache: 136) 
[9:9:7, Fri Jul 23, 2010] [0] Memory usage at idle thread 1: 253.804544MB (obj cache: 136) 
[9:9:9, Fri Jul 23, 2010] [0] 4 - Creating a new obj 
[9:9:9, Fri Jul 23, 2010] [301] Sending data to thread 
[9:9:9, Fri Jul 23, 2010] [0] 3 - Creating a new obj 
[9:9:9, Fri Jul 23, 2010] [302] Sending data to thread 
[9:9:9, Fri Jul 23, 2010] [0] 1 - Creating a new obj 
[9:9:10, Fri Jul 23, 2010] [0] 3 - Creating a new obj 
[9:9:11, Fri Jul 23, 2010] [0] 3 - Creating a new obj 
[9:9:11, Fri Jul 23, 2010] [0] Memory usage at end thread 4: 253.93152MB (obj cache: 136) 
[9:9:11, Fri Jul 23, 2010] [0] Memory usage at idle thread 4: 253.93152MB (obj cache: 136) 
[9:9:12, Fri Jul 23, 2010] [308] Sending data to thread 
[9:9:12, Fri Jul 23, 2010] [0] 4 - Creating a new obj 
[9:9:13, Fri Jul 23, 2010] [0] Memory usage at end thread 1: 253.804544MB (obj cache: 136) 
[9:9:13, Fri Jul 23, 2010] [0] Memory usage at idle thread 1: 253.804544MB (obj cache: 136) 
[9:9:14, Fri Jul 23, 2010] [0] Memory usage at end thread 4: 253.804544MB (obj cache: 136) 
[9:9:14, Fri Jul 23, 2010] [0] Memory usage at idle thread 4: 253.804544MB (obj cache: 136) 
[9:9:14, Fri Jul 23, 2010] [0] Memory usage at end thread 3: 253.93152MB (obj cache: 136) 
[9:9:14, Fri Jul 23, 2010] [0] Memory usage at idle thread 3: 253.93152MB (obj cache: 136) 
[9:9:15, Fri Jul 23, 2010] [313] Sending data to thread 
[9:9:15, Fri Jul 23, 2010] [0] 1 - Creating a new obj 
[9:9:16, Fri Jul 23, 2010] [0] Memory usage at end thread 2: 214.482944MB (obj cache: 136) 
[9:9:16, Fri Jul 23, 2010] [0] Memory usage at idle thread 2: 214.482944MB (obj cache: 136) 
[9:9:16, Fri Jul 23, 2010] [315] Sending data to thread 
[9:9:16, Fri Jul 23, 2010] [0] 4 - Creating a new obj 
[9:9:17, Fri Jul 23, 2010] [0] Memory usage at end thread 1: 214.355968MB (obj cache: 136) 
[9:9:17, Fri Jul 23, 2010] [0] Memory usage at idle thread 1: 214.355968MB (obj cache: 136) 
[9:9:18, Fri Jul 23, 2010] [316] Sending data to thread 
[9:9:18, Fri Jul 23, 2010] [0] 3 - Creating a new obj 
[9:9:18, Fri Jul 23, 2010] [317] Sending data to thread 
[9:9:18, Fri Jul 23, 2010] [0] 2 - Creating a new obj 
[9:9:18, Fri Jul 23, 2010] [318] Sending data to thread 
[9:9:18, Fri Jul 23, 2010] [0] 1 - Creating a new obj 
[9:9:19, Fri Jul 23, 2010] [0] Memory usage at end thread 4: 214.355968MB (obj cache: 136) 
[9:9:19, Fri Jul 23, 2010] [0] Memory usage at idle thread 4: 214.355968MB (obj cache: 136) 
[9:9:19, Fri Jul 23, 2010] [0] Memory usage at end thread 1: 214.355968MB (obj cache: 136) 
[9:9:19, Fri Jul 23, 2010] [0] Memory usage at idle thread 1: 214.355968MB (obj cache: 136) 
[9:9:20, Fri Jul 23, 2010] [0] Memory usage at end thread 3: 214.482944MB (obj cache: 136) 
[9:9:20, Fri Jul 23, 2010] [0] Memory usage at idle thread 3: 214.482944MB (obj cache: 136) 
[9:9:20, Fri Jul 23, 2010] [0] Memory usage at end thread 2: 214.482944MB (obj cache: 136) 
[9:9:20, Fri Jul 23, 2010] [0] Memory usage at idle thread 2: 214.482944MB (obj cache: 136) 

UPDATE (8/12/2010): Просто побежал за один день с новой скомпилированной версии Perl 5.12 с потоками и системы таНос. Странно, я получаю такое же поведение. Потеряйте кусочки МБ за раз, медленно. Можете попробовать Valgrind, чтобы понять, почему я теряю его. Пока я играл с чем-то другим, хотя я думал о чем-то другом. Мой скрипт создает и уничтожает (предположительно) много SSL-сокетов. Возможно ли, что широко используемый модуль, такой как IO :: Socket :: SSL, немного течет? Или, может быть, OpenSSL? (Использование v0.9.8o). Чтобы попробовать синхронизировать доступ к модулю SSL, чтобы увидеть, имеет ли он какой-либо эффект, могут возникнуть проблемы с доступом к ним.

ОБНОВЛЕНИЕ: Пробная загрузка модулей отдельно в каждом потоке, более быстрое использование памяти. Пробовал блокировать области с помощью функций сокетов, поэтому только один поток за раз использовал их, все еще потерял память так же, как и раньше. Увеличилось число рабочих потоков с 4 до 10 с одинаковым объемом работы. Память не длилась 30 минут. Позволяет мне думать, что это либо проблема Perl внутри страны с реализацией потока, либо проблема стека (каламбур не предназначен). Я попытался изменить размер стека, используя встроенные методы потока, но тот же результат. Идти искать другой путь. Возможно, более низкий уровень. Увеличение количества потоков заставляет память ускоряться ... похоже, что-то с реализацией стека нитей или размером стека

ОБНОВЛЕНИЕ (9/15/2010): Обнаружил этот интересный лабиринт в IO :: Socket :: SSL doc ...

Это связано с тем, что для того, чтобы сделать IO :: Socket :: SSL-сокеты, действуют одновременно как объекты и ссылки на glob.

"Циркулярная ссылка" да? Еще одно возможное объяснение заключается в том, что эти сокеты все время прилипают к нему, хотя я явно не отказался от них. Заглядывайте в Weaken, чтобы узнать, не делает ли это что-либо с сокетами. Сообщите, найду ли я что-нибудь интересное.

РЕШИТЬ (9/16/2010): Смотрите мой ответ, что я отправил, содержащий раствор

+0

Опубликовать код или найти круглые ссылки: P –

+1

Вы посмотрели на этот вопрос: http://stackoverflow.com/questions/429254/how-can-i-find-memory-leaks-in-long-running -perl-program – dwarring

+0

Возможный дубликат [Использование профилей памяти Perl и обнаружение утечки?] (http://stackoverflow.com/questions/1359771/perl-memory-usage-profiling-and-leak-detection) – Ether

ответ

5

Наконец-то пригвоздили утечку. Во-первых, я хотел бы показать вам улучшение. Вы не должны смотреть на фактические цифры, потому что пользовательская база увеличилась с момента первого графика, просто посмотрите на разницу в наклоне.

перед использованием памяти графа: http://i32.tinypic.com/311nc08.jpg
после окончания срока использования памяти графа: http://i51.tinypic.com/29goill.jpg

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

Каждый учебник, пример, презентация и книга, которые я использовал для разработки сервера, все опустили очень важный факт в отношении IO :: Socket :: SSL. И все, кто использует этот модуль в поточном приложении, лучше прослушивают. Никто никогда не подчеркивал одну из последних строк в документации IO :: Socket :: SSL, из-за которой я очень глупо предполагал, что любой сокет, который я создаю, как и любая другая структура данных, будет освобожден после того, как он выйдет за рамки (И да , Я знаю исключения из этого правила).Я думал, что сделаю все одолжение и вызову ту линию, о которой я говорю.

... IO :: Socket :: Сокеты SSL будут оставаться открытыми до тех пор, пока программа не закончится или вы их явно не закроете. Это связано с тем, что для создания IO :: Socket :: SSL сокетов действуют циклические ссылки, аналогичные объектам и ссылкам на glob.
http://search.cpan.org/dist/IO-Socket-SSL/SSL.pm#LIMITATIONS

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

Как вы можете себе представить, это было очень простое решение. Внутри цикла работы потока, где сокет создается каждый раз итерации, я просто разместил eval { close $socket; };undef $socket; внизу, чтобы убедиться, что он закрыт перед обработкой следующего клиента. Я запустил свой сервер и ждал и смотрел, как использование памяти было прочным, как вы можете видеть на моем втором графике. Поэтому после двух месяцев устранения и устранения этой проблемы я, наконец, пришел к решению. Надеюсь, что это дает некоторое представление любому программисту с сокетами. Благодаря всем, кто предоставил ответы/комментарии/предложения, каждый бит помог, и это помогло найти место, где можно было бы отказаться от идей.

+0

Поздравляем! Мне кажется, что dev из 'IO :: Socket :: SSL' должен' sub CLONE {warn 'читать раздел LIMITATION docs.} И потребовать от вас отказаться от его отключения. Я не вижу причин, по которым когда-либо требуется круговой рефлекс. Я бы поспорил, что это было написано C-хакером go perl, а не perl wiz. Создайте отчет об ошибке и нажмите, чтобы зафиксировать его или добавить больше комментариев. –

+0

Я бы хотел добавить, это звучит неправильно, он говорит, что если у вас есть «Scalar :: Util», этого не произойдет. И у вас есть «Scalar :: Util», если вы используете '5.8.8' - и особенно если вы обновили. Может быть, вам стоит сука из сундуков в Red Hat или любого другого дистрибутива, который решил отправить без базовых модулей ... Либо это, либо на ваши номера, это неправильный код. Я все еще мог видеть, что модуль является более подробным. –

+0

@Evan Я установил стандартный дистрибутив из источника, обновленный до 5.12.1. Он действительно поставляется с Scalar :: Util, но по какой-то причине он не помогал. Я попробовал обновить Scalar :: Util до последней версии, а также установить (как говорит документ, также поможет) WeakenRef. Все еще была та же проблема. Очень странно, но, наконец, просто вручную закрывая их все, прежде чем выйти из сферы действия, сделал трюк. Вот почему я забыл об этих модулях, потому что они, похоже, не помогли мне, но да, они должны позаботиться об этом для вас. – casey

15

Ваш Perl 4 с половиной лет. Обновите до 5.12. Просто найдите 5,12 сборки заметки и смотреть на дерьмо тонну улучшений основных нитей имеет, что может быть просто волшебным исправить туманную проблему:

  • 5,12 :: @_ и $ _ больше не просочиться под потоками (RT # 34342 и # 41138, также # 70602, # 70974) '
  • 5.10 :: Под ithreads регулярное выражение в PL_reg_curpm теперь подсчитывается по ссылке. Это устраняет много хакерских обходных решений, чтобы справиться с этим, не считая ссылки.
  • 5.9 :: threads: несколько исправлений, например, для проблем соединения() и утечек памяти. На некоторых платформах (например, Linux), которые используют glibc, минимальный объем памяти одной и т. Д. Сократился на несколько сотен килобайт.
  • 5.9 :: threads :: shared Многие утечки памяти были исправлены.

Я имею в виду список просто идет о том, когда вы говорите о четырех лет развития THEAD и широкий спектр вещей, которые могут вызвать эту проблему, проверьте современный журнал изменений в threads::shared

Я прокомментировал ваш пост , это будет мой следующий набор предложений: если вы не используете glibc, а используете perl malloc (по умолчанию), вы никогда не будете выпускать память для ОС. Размер процесса будет представлять максимальный размер perl, который будет использоваться каждый раз. Попробуйте перестроить с помощью glibc malloc (требуется перекомпиляция), посмотрите, поддерживает ли это другой профиль памяти. Кроме того, настало время показать код.

+0

Почему это было downvoted? С таким вопросом этот ответ может быть полезен. – Konerak

+1

Это полезно. Я загрузил 5.10 на сервер, но еще не создал его. Я думаю, что я могу просто получить 5.12 и построить это вместо этого сейчас. Все еще интересно, может ли это быть проблемой размера стека по умолчанию? – casey

+3

Теперь я запускаю свой скрипт примерно на час на v5.12.1. Это был большой шаг в правильном направлении. Опубликовал свои результаты на вопрос – casey

1

Попробуйте обновить thread.pm и threads :: shared. Переход к perl 5.12.1 также является хорошей идеей.

+0

Да, я сначала обновил эти модули. Не очень помогло. Просто перестроил perl 5.12.1 и снова установил все модули. Подождите и посмотрите, как выглядит использование памяти за несколько часов. – casey

1

Я столкнулся с той же проблемой с 5.10 и, несмотря на многочисленные протесты, что новый Perl не утечка памяти при использовании потоков, Perl просочился в память при использовании потоков.

Моим решением было использовать Thread::Pool::Simple, чтобы создать пул потоков и использовать это вместо создания новых потоков. В моем случае я не ожидал, что у меня будет много одновременных потоков, а также для них долгое время (возможно, максимум 30 секунд). Я не знаю, будет ли это для вас вариантом, но если вы действительно используете свои потоки только для простых задач, это может быть так.

+0

Проблема с этим. У меня есть десять одновременных потоков, которые всегда работают. Я не создаю и не уничтожаю никаких после начального 10. Поэтому мне не нужен объект менеджера пулов, например Thread :: Pool. Но, если вы говорите, что переход на Thread :: Pool спас вас от утечки памяти, тогда я отдам это. Мое второе колебание в том, что я должен создать все свои темы в начале и не уверен, что это позволяет мне. Может быть, у вас есть такая информация. Память родительского процесса становится слишком большой, и я хочу, чтобы потоки были только рабочими. – casey

+0

В моей ситуации было много нитей, приходящих и идущих, а не затянувшихся, так что сработало для меня, возможно, не сработало для вас. Проблема, которую я заметил, что создание потоков, даже если они не сделали никакой работы и немедленно вышло, пропустит небольшую память. Через некоторое время он начинает складываться до реального использования. Я не слишком хорошо знаком с реализацией Thread :: Pool :: Simple, но у него есть опции «min» и «max», которые подсказывают мне, что он создает потоки «min» при запуске пула, но они не выполняют никакой работы, пока вы не назовете метод добавления «пула» пула. – Phil

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