Perl потоки медленно потребляют память - PullRequest
9 голосов
/ 15 июля 2010

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

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

Я использую Perl 5.8.8, собранный из исходного кода в Linux 2.6

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

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

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

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

Есть еще идеи? Кто-нибудь вообще мог попробовать? Я уже отменяю все свои переменные.

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

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

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

ОБНОВЛЕНИЕ: Вот выдержка из журнала, документирующего свободную память до и после каждого потока послеоколо минутыМожет быть, это поможет кому-то лучше понять проблему.Кажется, что он немного стабилен, а потом время от времени съедает больше памяти.Здесь я теряю почти 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)

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

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

ОБНОВЛЕНИЕ (15.09.2010): Нашел этот интересный кусок в документе IO :: Socket :: SSL ...

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

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

РЕШЕНО (16.09.2010): См. Мой ответ, который я отправил, содержащий решение

Ответы [ 4 ]

15 голосов
/ 15 июля 2010

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

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

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

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

6 голосов
/ 17 сентября 2010

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

График использования памяти до: 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 действовали как циклические ссылки на объекты и глобальные ссылки, требуется циклическая ссылка.
http://search.cpan.org/dist/IO-Socket-SSL/SSL.pm#LIMITATIONS

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

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

1 голос
/ 15 июля 2010

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

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

1 голос
/ 15 июля 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...