Встроенные средства контроля ресурсов используемой оперативной памяти в приложении
Описывается достаточно простое в реализации программное средство контроля используемых ресурсов оперативной памяти в процессе выполнения приложения. Основу реализации составляет перехват и регистрация запросов на выделение, освобождение и повторное использование ресурсов памяти, направляемых приложением операционной системе через вызовы malloc(), calloc(), realloc(), free(). Все запросы памяти регистрируются в специальном журнале и по завершении приложения накопленная информация выводится в форме отчета на консоль или записывается в текстовый файл. Анализ отчета позволяет выявлять случаи неэффективного использования оперативной памяти в приложении. К таковым относятся “утечки” (memory leaks), когда запрошенные ресурсы памяти не освобождаются и не востребуются приложением, фрагментация, когда размеры освобожденных и доступных для повторного использования непрерывных участков памяти оказываются недостаточными для удовлетворения новых запросов, что приводит к выделению дополнительных ресурсов.
Встраиваемое средство контроля оперативной памяти может находиться во включенном или выключенном состояниях (на регистрацию запросов памяти), а возможности программного переключения состояний позволяют управлять и оптимизировать объем получаемых для данных по выделенным ресурсам памяти.
Введение
Значительная доля современных программных комплексов разрабатывается c использованием языков программирования C#, Java и им подобных, где управление ресурсами оперативной памяти выполняется на системном уровне с минимизацией ответственности разработчиков программного обеспечения. В качестве примера такого рода программных комплексов можно назвать систему сквозного автоматизированного проектирования электронной аппаратуры Delta Design, реализованную на C# в среде .net. С другой стороны, программные средства систем автоматизации обладают достаточно длительным “сроком жизни” (до нескольких десятков лет), что приводит к необходимости поддержания “устаревающих” технологий управления ресурсами оперативной памяти, в частности, при интеграции двух упомянутых классов систем в общую схему управления.
По этой причине до настоящего времени актуальной проблемой при создании, развитии и со-провождении функционально сложных программных приложений остается необходимость оптимизации управления в них ресурсами оперативной памяти. Наряду с эффективными методами (умные указатели, сборщики “мусора”) по-прежнему продолжают использоваться программные средства, выполняющие запросы ресурсов памяти через вызовы malloc, calloc, realloc, free.
Такой подход предоставляет разработчикам полный контроль по управлению оперативной памятью в приложении, одновременно возлагая на него высокий уровень ответственности за своевременное освобождение этих ресурсов во избежание “утечек”.
Как правило, на начальных этапах разработки программных средств задача выявления фактов неэффективного использования ресурсов памяти не является приоритетной, так как структура приложения является достаточно прозрачной. Однако проблемы начинают проявляться при интеграции различных структурно-функциональных составляющих системы со сложными схемами распределенного управления ресурсами оперативной памяти и обмена информации между ними. Смена поколений разработчиков системы также оказывает свое влияние на решение вопросов оптимизации управления ресурсами оперативной памяти. Как следствие, в приложении начинают проявляться следующие негативные свойства:
- обнаруживается значительный разрыв между реальными размерами потребляемой оперативной памяти и оценками разработчиков;
- наблюдается линейный рост используемой динамической памяти в процессе выполнения приложения.
Причинами неэффективного использования оперативной памяти в приложении являются раз-личные факторы, которые являются композициями следующих элементарных событий:
- несоответствие запросов на размещение и освобождение оперативной памяти (“утечки”);
- низкий уровень повторного использования освобожденных участков оперативной памяти, что ведет к запросам новых участков (фрагментация).
Практика разработки и сопровождения программных приложений со сложной архитектурой и развитой функциональностью показывает, что контроль за использованием ресурсов оперативной памяти в них следует начинать как можно раньше для сохранения управляемости этим процессом. Контроль за ресурсами памяти приложении можно осуществлять путем приобретения и применения соответствующих программных продуктов (например, valgrind), либо по-средством реализации и встраивания в программный код приложения средств перехвата и регистрации запросов к ресурсам оперативной памяти.
Ниже представляется пример одной из возможных реализаций такого подхода.
Программные средства встроенного контроля
Основу представляемого решения составляет перехват средствами встроенного контроля всех запросов к ресурсам оперативной памяти, направляемым приложением операционной системе в процессе его исполнения. Все многообразие таких запросов осуществляется через вызовы malloc(), realloc() или free(). Пример последовательности событий при обработке запроса на вы-деление оперативной памяти malloc() при отключенном и включенном состояниях средств встроенного контроля представлен на диаграмме ниже (аналогичные диаграммы могут быть построены для realloc() или free() вызовов). Полоса “A” включает последовательность событий при выделении памяти, происходящих в отключенном состоянии режима встроенного контроля. Приложение направляет операционной системе запрос на выделение требуемого размера памяти и получает указатель на адрес начала выделенного фрагмента. В том случае, когда запрошенный размер памяти недоступен возвращается указатель с нулевым адресом, что соответствующим образом должно обрабатываться запрашивающим приложением.
Полоса “B” включает последовательность событий при выделении памяти, происходящих при включенном режиме встроенного контроля. Эта последовательность полностью совпадает с описанной ранее лишь с тем исключением, что после выделения запрошенной памяти управ-ление передается в функцию register_request(), которая выполняет сохранение информации об адресе и размере выделенной памяти по сделанному запросу.
Собственно перехват и регистрация запросов к ресурсам оперативной памяти выполняется объектом memSupervisor, который создается в единственном экземпляре посредством вызова init_memSupervisor() и который затем может переключаться во включенное или выключенное состояния через вызовы enable_memSupervisor() или disable_ memSupervisor() соответственно.
Техника перехвата запросов к ресурсам оперативной памяти основывается на использовании статических переменных GNU библиотеки как это показано в следующих фрагментах кода.
Все обработанные запросы ресурсов оперативной памяти регистрируются в специальной таблице записей, содержание которой хранится в memSupervisor.memRegister атрибуте. Каждая запись содержит следующую информацию:
Каждая запись в таблице ассоциируется с контрольной точкой программного кода, после про-хождения которой выполнено выделение памяти и регистрация запроса.
Генератор текстовых отчетов (memSupervisor.genReport(имя__файла)) формирует отчет по данным таблицы и записью информации в указанный текстовый файл (или выводом на консоль приложения). Загрузка отчетных данных в таблицу MS Excel и соответствующая настройка последнего позволит получить графическое представление данных отчета по использованию ресурсов оперативной памяти.
Интерфейс к средствам встроенного контроля
Сбор информации о запросах приложения к ресурсам оперативной памяти может быть реализовано путем составления и размещения в исходном коде приложения макросов, компилируемых только при установке соответствующей переменной среды окружения.
Ниже представлен фрагмент программного кода, демонстрирующего применение средств встроенного контроля.
Ниже представлена иллюстрация применения описанных средств встроенного контроля ресурсов оперативной памяти, запрашиваемых ‘foo’ приложением, выполняющим автоматическую трассировку соединений на печатных платах. Для анализа выбрана достаточно наглядная и удобная модель меандра, отображающего размеры запрошенной приложением динамической оперативной памяти в процессе выполнения им прокладки соединений на печатной плате.
Эта модель помогает выявить следующие проблемы управления ресурсами оперативной памя-ти:
- в точках “A” и “D” должны быть примерно одинаковые размеры используемой оперативной памяти
- рост размеров запрашиваемой памяти на этапе выполнения прокладки соединений дол-жен быть плавным. При необходимости, основные шаги этого этапа должны контролироваться путем установки контрольных точек в соответствующих фрагментах программного кода. Принудительное удаление всех данных о проложенных соединениях должно приводить к одинаковым размерам потребляемой памяти в точках “B” и “C”
Диаграмма ниже показывает проблемы управления ресурсами оперативной памяти в упомяну-том ‘foo’ приложении.
Краткие выводы
Описанные средства встроенного контроля ресурсов оперативной памяти могут быть использованы в случаях, когда соответствующие программные продукты недоступны и неприменимы по каким–либо причинам.
- Средства встроенного контроля регистрируют и накапливают информацию о ресурсах запрошенной памяти с любой степенью точности и детализации;
- Возможности переключения средств встроенного контроля между активным и неактивным режимами позволяют настраивать их на извлечение и анализ данных в выбранных фрагментах программного кода приложения;
- Встроенные средства контроля допускают полное их исключение из приложения при сборке последнего без установки соответствующего параметра компиляции;
- Реализация программного анализа содержания получаемых при исполнении приложе-ний отчетов об использованных ресурсах оперативной памяти и сверка последних с эталонными копиями позволяет разработать процедуры регрессионного тестирования приложений на предмет обнаружения в них деградации в использовании оперативной памяти;
- Программный код такого рода средств встроенного контроля оперативной памяти от-крыт для расширения его возможностей и адаптации к особенностям условий его при-менения;
- Основным недостатком описанных средств является необходимость включения кода встроенного контроля оперативной памяти в код контролируемого приложения.
В заключение необходимо отметить, что описанный подход по реализации встроенного кон-троля ресурсов оперативной памяти в программных приложениях не содержит каких-либо кардинально новых решений. Полезность подхода заключается в простоте реализации и доста-точно высокой эффективности практического применения.