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

UnixForum





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

MemShrink

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

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

Обзор архитектуры

Для понимания проблем, с которыми мы столкнулись, и решений, которые мы выработали, вам потребуются базовые знания о том, чем занимается и как работает приложение Firefox.

Современный веб-браузер по своей сути является виртуальной машиной для исполнения недоверенного кода. Этот код является некоторой комбинацией данных HTML, CSS и JavaScript (JS), полученных из сторонних источников. Также используется код дополнений и плагинов Firefox. Виртуальная машина позволяет выполнять вычисления, размещение и стилизацию текста, изображений, осуществлять доступ к сетевым ресурсам, хранить данные в локальном хранилище и даже получать доступ к аппаратным возможностям ускорения вывода графики. Для некоторых из этих возможностей предоставляются API, спроектированные в соответствии с поставленной задачей; многие другие возможности доступны через API, которые были переработаны для функционирования в рамках совершенно отличающихся от первоначальных сценариев использования. Учитывая путь эволюции веб-стандартов, веб-браузеры должны быть очень либеральными в плане принимаемых к обработке документов, причем документы, для обработки которых веб-браузеры создавались 15 лет назад, могут стать препятствием для достижения высокой производительности браузера в наши дни.

В основе Firefox лежит система вывода документа Gecko и система обработки сценариев JS Spidermonkey. Обе системы разрабатывались в первую очередь для использования в составе Firefox, но при этом являются отдельными программными компонентами, допускающими независимое повторное использование. Как и все широко используемые системы вывода документов и обработки сценариев JS, они разработаны с использованием языка программирования C++. В рамках проекта Spidermonkey реализована виртуальная машина JS, поддерживающая механизм сборки мусора и множество технологий непосредственной компиляции (JIT). В рамках проекта Gecko реализована большая часть API, доступных для веб-страницы, включая API для модели DOM, вывода графики с помощью программных или аппаратных конвейеров, вывода страниц и текста, полной реализации сетевого стека, и множества других механизмов. Вместе эти компоненты реализуют платформу, на основе которой и разработан веб-браузер Firefox. Пользовательский интерфейс веб-браузера Firefox, включая адресную строку и кнопки навигации, является всего лишь набором специальных веб-страниц, которые открываются с расширенными привилегиями. Эти привилегии позволяют им осуществлять доступ к всем типам возможностей, которые недоступны для обычных страниц. Мы используем для обозначения этих специальных встроенных привилегированных страниц термин chrome (не имеющий отношения к браузеру Google Chrome) в отличие от обычных веб-страниц, для обозначения которых используется термин content.

Для нас наиболее интересными подробностями реализации Spidermonkey и Gecko являются их механизмы управления памятью. Мы можем классифицировать используемую браузером память на основе двух характеристик: как она резервируется и как она освобождается. Динамически резервируемая память (память из кучи - heap) принимается большими фрагментами от операционной системы и делится на фрагменты необходимого размера системой резервирования памяти. Существуют две основных системы резервирования памяти: специализированная система резервирования памяти с поддержкой механизма сборки и мусора (GC heap) в Spidermonkey и система jemalloc, которая используется в остальных местах в Spidermonkey и Gecko. Также существуют три метода освобождения памяти: ручное освобождение, освобождение с помощью подсчета ссылок и освобождение с помощью механизма сборки мусора.

Управление памятью в Firefox
Рисунок 5.1 - Управление памятью в Firefox

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

В Gecko для большей части памяти используются счетчики ссылок. При использовании этой технологии осуществляется отслеживание количества ссылок на заданный фрагмент памяти. В момент, когда это количество становится равным нулю, память освобождается. Хотя подсчет ссылок технически является формой системы сборки мусора, в данном описании мы будем отделять его от схем сборки мусора, которым требуется специализированный код (т.е., сборщик мусора) для периодического освобождения памяти. Простой механизм подсчета ссылок не может работать циклическими ссылками, когда один фрагмент памяти A ссылается на другой фрагмент памяти B и наоборот. В этой ситуации и фрагмент A, и фрагмент B имеют по одной ссылке и никогда не будут освобождены. В рамках Gecko существует специализированный трассирующий сборщик мусора, предназначенный специально для освобождения памяти при работе такими циклическими ссылками, который мы называем циклическим сборщиком мусора (cycle collector). Этот циклический сборщик мусора работает только с определенными классами, которые создают циклические ссылки и регистрируются в наборе циклических классов, поэтому мы можем рассматривать циклический сборщик мусора как подвид сборщика мусора на основе счетчика ссылок. Циклический сборщик мусора также взаимодействует со сборщиком мусора из состава Spidermonkey для реализации межъязыкового механизма управления памятью, который позволяет хранить ссылки на объекты JS в коде C++ и наоборот.

И в Spidermonkey, и в Gecko также существует большое количество фрагментов памяти, управление которыми осуществляется в ручном режиме. Эта память включает в себя все фрагменты, начиная с фрагментов внутренней памяти для хранения массивов и хэш-таблиц и заканчивая фрагментами для хранения данных изображений и исходного кода сценариев. Существуют также другие специализированные системы резервирования памяти, созданные на основе резервируемых в ручном режиме фрагментов памяти. Одним из примеров является система резервирования арен (arena allocator). Арены используются в том случае, когда большое количество фрагментов памяти из основной кучи может быть освобождено одновременно. Система резервирования арен получает фрагменты памяти от основной системы резервирования памяти и разделяет их в соответствии с требованиями. В момент, когда арена больше не нужна, эти фрагменты возвращаются в основную кучу без необходимости отдельного освобождения множества небольших фрагментов. Gecko использует систему резервирования арен для резервирования памяти под данные страницы, причем эта память может быть освобождена сразу после того, как отпадет необходимость в просмотре страницы. Система резервирования арен также позволяет нам реализовывать такие механизмы безопасности, как уничтожение данных (poisoning), когда мы перезаписываем освобожденные фрагменты памяти для предотвращения доступа к сохраненным в них до этого данных.

Существует также несколько других специальных систем управления памятью, используемых в небольших частях веб-браузера Firefox по множеству различных причин, но они не относятся к нашему предмету разговора. Теперь, когда вы вкратце ознакомились с архитектурой систем управления памятью в Firefox, мы можем обсудить обнаруженные нами проблемы и методы их решения.


Продолжение статьи: Вы работаете с тем, что вы можете измерить.