Pull to refresh

Охота на утечки

Reading time3 min
Views2K
В даном посте я описал свой опыт борьбы с одной конкретной утечкой в большом проекте. Было допущено много ошибок, но может кому то будет полезно.

Давно не сталкивался с утечками памяти, но на днях наш плюсовый демон начал течь. При чем valgrind.memcheck не показывает ничего внятного — сервер долго стартует, большую нагрузку дать не получается — watchdog, который проверяет на зависание сервера при такой нагрузке под валгриндом — резво прибивает сервер.При выходе из под маленькой нагрузке — ликов нет.
Течет сильно — 7 гигов за 3 дня. Хотя раньше в рабочем режиме больше 1 гига не требовал (был запущен и по 2 недели без перезапусков).



Подумалось, что либо циклические ссылки, либо данные с какого то контейнера не удаляются.
Ну что же решил заюзать старый добрый google perf tools, что бы сделать снимок памяти в момент работы. Хм. Под ubuntu amd64 почему то только старая версия. И она виснет когда приложение разпаралеливается (хотя google perf tools — специально сделано для многопоточных приложений).
В дебагере видно, что все потоки висят на спинлоках.

Решил обновиться до последней версии — но компились версию с сайта не хотелось. Нашел пакеты на packages.debian.org — и на удивление всё установилось без проблем.
Но при входе в многопоточный режим работы демона — опять зависание.
Да и сам демон загружается под 15-20 минут — так как взял базу с продакшена, а там данных очень много.

Поискал какие то хорошие аллокаторы для отладки — того что мне надо было (google perf tools — heap peofiler, который бы не вис) не было.
Ну а может в valgrind.memcheck есть какие то ключи, что бы дампить состояние памяти не в конце работы программы, а в середине? Нет. Но… Есть valgrind.massif — который этим и занимается. Ура!

Безжалостно очистив базу от данных (так что бы хоть нагрузочные тесты могли работать, а они требуют настоящую игру и хоть немного валидных данных), стал ковырять valgrind.massif.
Запустил — прогнал нагрузочные тесты — всё работает, ничего не течет. Очень интерестно.
В общем тулзы мне никакого ответа не дали — наверно синтетическая нагрузка не провоцировала ошибку, влекущую утечку памяти. Надо включить моск;)
К сожалению с последней стабильной выливки очнь много кода поменялось и я тому виной: делал рефакторинг затронув почти половину всего кода;)

Думал задампить память и посмотреть что там, но разбираться в 7 гиговом файлике не хотелось. И тут — пришла безумная идея — анализировать статистику по демонам (благо текущих демона у нас 3 в кластере) которую мог собрать как с операционки, так и с внутреннего мониторинга демонов.

Совершенно случайно увидел пропорцию между количеством утекшей памяти и количеством выполненных команд одного типа. Команда была старой, но я вспомнил, что в эту выливку мы её стали намного активнее использовать. Проверил пропорцию на калькуляторе (питон в шеле;) — всё совпадает. Эта твать жрет память.

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

Добавив в нагрузочный тест эту команду — за 10 секунд отожрал 50% памяти своего ноута. А дальше дело техники — даже не стал использовать massif — почитал код — нашел место где может не создаваться циклическая ссылка — сменил policy с Strong на Weak — сбилдил — запустил тест — ура, жучек умер.

Ну и какие я сделал выводы из убитого дня на дебаг? Собственно вот:
  1. Надо чаще прогонять тесты на утечки памяти
  2. Даже если утечка маленькая — в будущем код может начать использоваться активнее — тогда маленькая утечка превратится в сильную головную боль
  3. Надо иметь репрезентативную базу по функционалу аналогичную продакшену, но с меньшим объемом данных. Без этого отладка под дебагерами памяти будет практически невозможна
  4. Ещё раз убедился — отсутствие голых указателей не гарантирует отсутствие утечек (это касается как java и c#, так и если использовать умные указатели в С++)
  5. Когда ничего не помогает — случайность может решить всё
  6. Случайности не случайны © =) — не ища какие то закономерности в статистиках я бы не нашел их, хотя я и не надеялся что то найти
Tags:
Hubs:
Total votes 7: ↑5 and ↓2+3
Comments17

Articles