Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

MemShrink

Глава 5 из книги "Производительность приложений с открытым исходным кодом".

Оригинал: MemShrink
Автор: Kyle Huey
Перевод: А.Панин

Вы работаете с тем, что вы можете измерить

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

Все еще более осложняется с учетом того факта, что большая часть памяти из пространства памяти веб-браузера Firefox подвергается обработке с помощью системы сборки мусора какого-либо типа и, таким образом, более не используемая память не освобождается до момента следующего запуска системы сборки мусора. Мы будем свободно использовать термин "утечка" для описания любой ситуации, в результате которой веб-браузер Firefox будет использовать память менее эффективно, чем в моделируемой ситуации с умеренным использованием памяти. Этот метод применения термина полностью соответствует методу его применения нашими пользователями: большинство пользователей и даже веб-разработчиков не может точно определить, связано ли потребление больших объемов памяти с существующей утечкой памяти или с любым другим набором факторов, возникающих в ходе работы браузера.

На начальном этапе реализации проекта MemShrink мы не имели четкого представления об используемых браузером механизмах работы с памятью. Установление природы проблем с управлением памятью обычно требует использования таких сложных инструментов, как Massif или таких низкоуровневых инструментов, как GDB. Эти инструменты имеют ряд недостатков:
  • Они спроектированы для разработчиков и не достаточно просты в использовании.
  • Они не располагают информацией об устройстве Firefox (такой, как подробности реализации различных систем резервирования памяти).
  • Они не являются "непрерывно функционирующими" - вам придется использовать их в момент возникновения проблемы.

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

Первым инструментом из этого набора является страница about:memory. Впервые представленная в версии 3.6 веб-браузера Firefox, она изначально выводила простую статистику использования памяти, содержащую объем отображенной и использованной памяти. Позднее была добавлена возможность вывода других интересующих определенных разработчиков данных, таких, как объем памяти, используемой встроенной системой для работы с базами данных SQLite, а также объем памяти, используемой подсистемой ускорения вывода графики. Мы называем эти измерения докладчиками об использовании памяти (memory reporters). Если не учитывать эти простые дополнения, страница about:memory так и осталась примитивным инструментом, выводящим некоторые обобщенные статистические данные об использовании памяти. Большая часть механизмов управления памятью не имеет докладчиков об использовании памяти и не позволяет специальным образом подсчитывать объем памяти для вывода данных на страницу about:memory. Но даже в этом случае страница about:memory может использоваться любым человеком после простого ввода названия страницы в адресную строку без необходимости применения специального инструмента или повторной сборки Firefox. Эта страница имеет все шансы стать "непревзойденной возможностью".

Непосредственно перед тем, как на проект MemShrink стали возлагаться большие надежды, интерпретатор кода JavaScript в Firefox подвергся рефакторингу, направленному на разделение монолитного пространства памяти с механизмом сборки мусора на набор небольших подпространств, называемых подфрагментами. Эти подфрагменты используются для разделения таких фрагментов памяти, как фрагменты для хранения данных страниц chrome и content (привилегированный и непривилегированный код соответственно), а также память для хранения данных различных вебсайтов. Главной мотивацией для этого разделения были соображения безопасности, но оно также оказалось очень полезным для проекта MemShrink. Сразу же после реализации этого разделения мы создали инструмент с именем about:compartments, который выводил информацию обо всех подфрагментах памяти, а также о том, как много памяти и каким образом они используют. Страница about:compartments никогда не была интегрирована непосредственно в Firefox, но после начала проекта MemShrink она была модифицирована и объединена со страницей about:memory.

В процессе добавления отчетов об использовании подфрагментов памяти на страницу about:memory мы поняли, что внедрение аналогичного механизма создания отчетов для других систем резервирования памяти может оказаться полезным для профилирования системы резервирования памяти без применения таких специализированных инструментов, как Massif. Страница about:memory была изменена и теперь выводила наборы обобщенных статистических данных в древовидной форме, разделяя общую статистику использования памяти на отдельные статистические данные для памяти, используемой для различных целей. После этого мы начали добавлять докладчики об использовании памяти в другие типы систем резервирования больших объемов памяти, такие, как подсистема резервирования памяти для хранения данных страниц. Одной из наших первых задач в плане учета памяти было снижение объема не относящейся к определенной системе резервирования (heap-unclassified) памяти, которая не учитывалась докладчиком об использовании памяти. Мы выбрали по большей части произвольный показатель в 10% от общего объема памяти и решили снизить объем не относящейся к определенной системе резервирования памяти до этого объема в среднестатистических сценариях использования браузера. В итоге окажется, что запланированный показатель в 10% был чрезмерно малым. Просто существовало слишком много небольших резервируемых и освобождаемых фрагментов памяти для того, чтобы достичь стабильного показателя не относящейся к определенной системе резервирования памяти ниже примерно 15% от всей используемой памяти. Работа по сокращению объема не относящейся к определенной системе резервирования памяти улучшает понимание того, как браузер использует память.

Для сокращения объема не относящейся к определенной системе резервирования памяти мы разработали инструмент, названный детектором темной материи (Dark Matter Deetector - DMD), который помог отследить не учитываемые операции резервирования памяти. В процессе работы он подменяет основную систему резервирования памяти и добавляет информацию на страницу about:memory, сообщая о процессе резервирования блоков памяти и сопоставляя сообщения о резервировании блоков с зарезервированными блоками. Он обобщает операции резервирования памяти, для которых не создаются отчеты, на основе источников запросов. Запуск DMD в рамках сессии Firefox позволяет получить списки источников запросов, в результате которых создаются фрагменты не относящейся к определенной системе резервирования памяти. Как только источник всех запросов резервирования памяти определяется, поиск ответственного за резервирование памяти компонента и разработчика, который добавит в него код докладчика об использовании памяти, происходит достаточно быстро. После нескольких месяцев в нашем распоряжении был инструмент, который мог сообщить такие подробности, как "все страницы Facebook в вашем браузере используют 250 МБ памяти, ниже представлены подробности того, как именно эта память используется".

Мы также разработали другой инструмент (названный инструментом измерения и сохранения - Measure and Save) для исправления проблем с памятью сразу после их обнаружения. Этот инструмент позволяет сохранять представления как памяти интерпретатора JS, так и памяти циклического сборщика мусора для структур языка C++ в файл. После этого мы разработали множество сценариев для анализа, которые позволяют произвести обход объединенного пространства памяти и ответить на такие вопросы, как "что предотвращает уничтожение этого объекта?" Это позволило нам использовать множество полезных техник отладки, начиная с простого исследования графа фрагментов памяти в поисках ссылок, которые должны быть ликвидированы и заканчивая переходом в режим использования отладчика с возможностью расстановки точек останова в методах интересующих объектов.

Главным достоинством этих инструментов является то, что в отличие от таких инструментов, как Massif, вы можете ожидать проявления проблемы перед использованием инструмента. Многие системы профилирования памяти (включая Massif) должны быть запущены перед запуском программы, а не в ходе ее работы в момент проявления ошибки. Другим достоинством этих инструментов является возможность использования и анализа информации без необходимости непосредственного повторения проблемы. Вместе эти инструменты позволяют пользователям собрать информацию о наблюдаемой проблеме и отправить ее тем разработчикам, которые не могут самостоятельно добиться повторения проблемы. Ожидание от пользователей веб-браузера, даже имеющих опыт добавления сообщений об ошибках в систему отслеживания ошибок проекта, готовности применять GDB или Massif для отладки браузера обычно является слишком недальновидным. Но загрузка страницы about:memory или запуск небольшого фрагмента кода на языке JavaScript для получения данных, которые могут быть прикреплены к сообщению об ошибке, являются гораздо более простыми задачами. Системы профилирования памяти общего назначения собирают большой объем информации, но требуют значительных трудозатрат. В итоге нам удалось разработать набор инструментов, предназначенных для удовлетворения наших специфических потребностей и обладающих значительными преимуществами по сравнению с инструментами общего назначения.

Создание специфических инструментов не всегда разумно; по этой причине мы используем GDB вместо разработки нового отладчика для диагностики всего нашего программного обеспечения. Но в тех ситуациях, когда существующие инструменты не могут предоставить необходимую информацию предпочтительным способом, мы считаем предназначенные для определенной цели инструменты большим преимуществом. Нам потребовалось около года работы в течение неполного рабочего дня для того, чтобы привести страницу about:memory в завершенное состояние. Даже сегодня мы все еще добавляем новые возможности и докладчики об использовании памяти в случае необходимости. Специализированные инструменты являются важным нововведением. Более подробное описание подобных инструментов находится за рамками этой главы, скажем только, что вы должны тщательно оценивать преимущества и цену разработки специализированных инструментов перед их непосредственной разработкой.


Продолжение статьи: Низко висящий фрукт.