`

СПЕЦІАЛЬНІ
ПАРТНЕРИ
ПРОЕКТУ

Чи використовує ваша компанія ChatGPT в роботі?

BEST CIO

Определение наиболее профессиональных ИТ-управленцев, лидеров и экспертов в своих отраслях

Человек года

Кто внес наибольший вклад в развитие украинского ИТ-рынка.

Продукт года

Награды «Продукт года» еженедельника «Компьютерное обозрение» за наиболее выдающиеся ИТ-товары

 

Большие "незначительные утечки"

0 
 

В отличие от "потешного" характера предыдущей статьи, на этот раз обойдемся без шуток и сарказма. Слишком уж серьезен и болезнен в своих многочисленных проявлениях предмет разговора -- "утечка памяти" в программах. Впрочем, о важности мы еще поговорим, но до начала какого-либо обсуждения следует "на корню" предупредить потенциально возможное отношение к упомянутым инструментальным средствам как к "панацее" или даже просто как к "сильнодействующему лекарству". Увы, любые инструменты способны только иногда облегчить работу на определенных этапах создания и сопровождения программы. Выделенное "иногда" исключительно важно -- вероятны (скажем так, невероятно вероятны) случаи, когда эффективные инструменты, грамотно и правильно примененные, жизни совсем не облегчают. Так, например, тонкий runtime-анализ (анализ во время исполнения бинарного кода) программы с последующим тщательным "разбором полетов" может инициировать... просто отправление всего кода "на свалку" из-за непригодности архитектуры к модификациям -- такие прецеденты бывали не раз.


Надежность, Безопасность и Cº

Так как эта статья больше адресована рядовым программистам, не претендующим на лавры руководителей проектов и "гуру", совсем не лишним будет поговорить в ней о вещах, вроде бы не связанных с проблемами "рядового кодировщика", "зарытого" под многокилограммовыми томами документации по API, компонентным средам, инструментарию и алгоритмам. Если учесть и то, что целый ряд уже использованных терминов обладает нехорошим свойством "интуитивной понятности" (что в большинстве случаев означает полную утрату ими смысла и позволяет трактовать как угодно), то такое отступление не кажется излишним. И наконец, самое важное -- обсуждение терминов, как ни странно, дает нам возможность определиться с областью их применения -- т. е. с тем классом ПО, для которого поднятая тема наиболее актуальна.

Начнем с понятия "надежность программного обеспечения". Мы так привыкли им оперировать, что уже не придаем значения его смыслу. Иногда мы путаем "надежность" и "устойчивость к сбоям", иногда пытаемся измерять "надежность" в количестве сбоев в единицу времени, и т. д. Подобный "разнобой" легко объясним -- как ни странно, но "надежность" как качественное свойство программ просто не имеет количественной оценки. Являясь неформальным, оно выражает степень доверия к программе (в том числе и как к продукту). Такого взгляда придерживается не только автор прошедшей шесть изданий и ставшей классикой "Инженерии программного обеспечения" Йан Соммервил, но и подавляющее большинство специалистов, работающих в области, например, сверхустойчивого к сбоям ПО (в частности, М. Шуман в книге "Reliability of computer systems and networks").

Неформальное понятие "надежности" определяется на основании четырех главных критериев: работоспособности, безотказности, защищенности и безопасности. Работоспособность и без­отказность -- зависящие от времени фундаментальные характеристические свойства, первое из которых говорит о способности программы корректно работать в любое время, второе -- корректно работать всегда (т. е. в весь период активной эксплуатации ПО). Защищенность фактически дополняет два предыдущих понятия, устраняя упрятанную в них "идеальность" условий эксплуатации -- совершенная защищенная система должна быть и работоспособной, и безотказной даже при случайных или умышленных "помехах" ее нормальному эксплуатационному режиму. И наконец, безопасность. Эта экзотическая в потребительском ПО составляющая надежности гарантирует безопасность людям и окружающей среде при эксплуатации либо самой программы, либо более сложной системы, в которую она входит.

Мы, наконец, можем сделать первый самый важный вывод -- четыре эти составляющие фактически определяют степень доверия к программной системе. Последнее, очевидно, играет исключительную роль как для коммерческих разработок, так и для проектов Open Source -- программа, степень доверия к которой низка, может стать невостребованной, т. е. мертвой.

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

Мы же, наконец, подошли к моменту, когда можно определить класс ПО, для которого все наши рассуждения исключительно важны. Естественно, что класс потребительского ПО (иначе -- "десктопного") только с массой оговорок считается идеально соответствующим высоким требованиям к надежности. Здесь действительно важнее полезность при фиксированном уровне надежности, потому как востребованность программ в данном случае напрямую зависит от первой. Зато обширный класс программ server-side -- как раз та область, где степень доверия "потребителей" (у таких программ тоже есть свои потребители) фактически эквивалентна надежности.


Противная мелочь

"Утечка памяти" (memory leak) -- явление относительно распространенное и не обладающее свойством "гарантированного летального исхода" для программы. Заключается оно в том, что отдельные, запрошенные программой "для собственных нужд" области памяти "не возвращаются" операционной системе. ПО пользовательского класса самых разных платформ (включая "карманных малышей" -- Pocket PC и Palm) зачастую страдает подобными "болячками". Но как раз в пользовательских программах это именно "болячка" -- персональный компьютер (во всех его вариациях) владелец всегда может перезагрузить, а в случае с самым обычным десктопом вообще делает это довольно часто просто операцией выключения/включения. Соответственно и чувствительность к небольшим утечкам здесь невелика. Но когда речь идет о ПО server-side, все становится несравнимо хуже -- в этом мире обычны явления, когда сервер порождает и уничтожает несметное количество процессов (или потоков, что в нашем случае несущественно), и, не приведи Бог, их реализации будут страдать подобной "болячкой": вся система быстро исчерпает доступные ресурсы и, естественно, превратится в нефункционирующую. Вторая проблема не столь очевидна в формулировке, но факт ее существования подтвержден практикой -- если в программе есть memory leaks, с очень большой вероятностью в ней могут присутствовать и иные ошибки работы с памятью -- "утечки" являются четким признаком неаккуратного программирования. На деле это означает, что или можно отыскать способ вывода такой программы из строя (например, испробовать ее на устойчивость к переполнениям буферов), или есть потенциальная возможность использовать ее как "лазейку" к системным сервисам более высокого уровня.

Короче говоря, "утечки памяти" в программах server-side просто недопустимы.


Лекарственные средства

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

Превентивный способ борьбы заключается, естественно, в "грамотном проектировании и программировании". Вот только толкований "грамотности" существует нескончаемое количество, и вносить свою лепту в терминологический Вавилон нет никакого желания. И все-таки... Один из заслуженно считаемых эффективными "грамотных подходов" по сути является единожды принимаемым архитектурным решением. Речь идет об использовании "автоматических сборщиков мусора" (garbage collector), среди которых далеко не последнее место занимает легендарный распространяемый свободно в открытых исходных текстах кросс-платформенный GC. Будущее второго знаменитого garbage collector -- коммерческого Great Circle -- после приобретения фирмы-разработчика компанией Veritas сейчас не определено. Эффективность такого решения доказана многими большими проектами (например, Mozilla), но и оно никоим образом не является панацеей. Здесь есть свои подводные камни, главный из которых -- чувствительность "сборщиков мусора" к так называемым циклическим ссылкам (например, к сложным связанным структурам данных, в которых связи образуют замкнутое кольцо). Это не только "твердый орешек" для программ-сборщиков, но и источник потенциальных проблем, например в ходе отладки (не вдаваясь в подробности -- если динамическая сложная структура создается некоторым фрагментом программы на основании поступающей извне информации, при определенной комбинации алгоритма формирования и значений этих данных может произойти "зацикливание связей"). В остальном же "сборщики мусора" более чем эффективны (несмотря на массу "страшных" историй о снижении производительности использующих их программ и т. д.).

Второй "грамотный подход" -- прототипирование будущей программы с помощью интерпретируемого языка, максимально приближенного к применяемому в ходе разработки. К сожалению, техника прототипирования почему-то не находит многих приверженцев, несмотря на доступность инструментальных средств и их простоту. Такое ПО, как CINT (root.cern.ch/root/Cint.html, о нем мы говорили ранее в статье "Иной скриптинг") за счет интерпретируемого режима выполнения и максимальной близости к языкам С и С++ позволяет очень тонко исследовать "эскиз" программы со сложной логикой и пред­отвратить целый ряд неприятностей благодаря мощной информационной системе времени исполнения, присущей всем интерпретаторам.

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

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

Если "превентивные" методы борьбы и допускают инструментальную поддержку, но все-таки являются больше организационно-дисциплинарными, то в "постфактуме" одной самодисциплиной и культурой просто не обойтись. Тут главное -- инструменты, их возможности, качество и уровень владения ими разработчиком. Класс программ, которые можно отнести к "постфактум"-методу, очень обширен. Опять же, о классификации здесь и речь не идет, но все-таки напрашивается разделение его на два условных подкласса: "шпионы-резиденты" и "наблюдатели-аналитики". Различия между ними заключаются в принципе использования -- если представители первого должны быть "внедрены" в исходные коды программы, то вторые более "деликатны" и работают с собственно бинарным, исполняемым "продуктом". Соответственно, применение "шпионов-резидентов" может потребовать, например, дополнительных операций по трансляции и сборке специальной тестируемой версии программы (что в случае больших разработок более чем накладно), но может (опять же не всегда) дать исчерпывающую информацию о ее поведении. Хорошим примером развитого и доступного "шпиона-резидента" является ориентированная на С++-программистов отладочная библиотека libcwd. Возможности libcwd намного обширнее, чем просто выявление "утечек памяти", но на примере этого неплохо спроектированного инструмента легко понять, насколько сильно изменяется код приложения за счет "внедрения" "шпиона-резидента". Последнее означает, что, во-первых, проект системы должен предусматривать использование подобного средства, во-вторых, программист должен отлично знать "еще одну" библиотеку.

Представители подкласса "наблюдателей-аналитиков" более чем многочисленны. Необъятное количество их было создано на разнообразных Unix-платформах вследствие быстрого распространения этой ОС в университетской среде и культивируемого по сей день "хак-стиля" программирования. Не обижены их отсутствием и разработчики мира Windows -- кроме многих кросс-платформенных разработок им доступен бесплатный пакет Debugging Tools for Windows, в который входит утилита, идеально подходящая для выявления "утекания памяти" в прикладных программах -- UMDH (User Mode Dump Heap, umdh.exe), а также сервисная утилита Poolmon, позволяющая выявлять "утечки" в программах, работающих в режиме ядра.

Из многочисленных Unix-программ следует выделить отлично работающий "подменитель" стандартных процедур выделения/освобождения памяти Checker, имеющий, правда, целый ряд ограничений: требующий перекомпиляции всего проекта для создания тестовой исполняемой программы, ограниченный по используемым версиям компиляторов и платформам (Linux и Solaris).

Кросс-платформенные "наблюдатели-аналитики" -- самый интересный подкласс инструментов, позволяющий, в первую очередь, снизить немалые затраты на изучение при переходе на новую платформу. Здесь очень хороша легально бесплатная открытая библиотека-"подменитель" Dmalloc, практически идеальная для С-программистов, но все же хотелось бы привлечь внимание читателя к малоизвестной разработке, того заслуживающей. Библиотека Mpatrol в своем роде уникальна, созданная фактически одним человеком, она вполне способна конкурировать с мощными коммерческими аналогами. Mpatrol -- открытая программа, распространяемая на основе лицензии GNU и обладающая солидным перечнем весомых достоинств. Во-первых, в отличие от более простых аналогов (таких, как Dmalloc), она подходит и С-, и С++-программистам. Во-вторых, Mpatrol -- действительно кросс-платформенный инструмент (автор статьи без особых проблем использовал ее и на FreeBSD, Linux и Windows 2000, а полный список поддерживаемых 32- и 64-битных ОС, в которых Mpatrol работает, слишком велик для этой статьи). В-третьих, Mpatrol на большинстве платформ характеризуется свойством "thread-safe", что позволяет с ее помощью анализировать потоковые программы (здесь есть целый ряд нюансов, но в большинстве случаев они принципиально и несложно разрешимы). Ну и наконец, Mpatrol выдает более чем исчерпывающую информацию о работе с памятью анализируемой программы. К достоинствам этой библиотеки относится и то, что она практически образцово документирована и тривиальна в использовании (на многих платформах Mpatrol позволяет анализировать исполняемое немодифицированное приложение -- без изменений его исходных текстов и перекомпиляции).


P. S.
Идеей этой статьи автор обязан крохотной freeware-утилитке, незаметно "выевшей" к самому неподходящему времени всю память карманного компьютера, из-за чего не сработала программа-будильник и один день жизни начался с лишних проблем...

Ready, set, buy! Посібник для початківців - як придбати Copilot для Microsoft 365

0 
 

Напечатать Отправить другу

Читайте также

 

Ukraine

 

  •  Home  •  Ринок  •  IТ-директор  •  CloudComputing  •  Hard  •  Soft  •  Мережі  •  Безпека  •  Наука  •  IoT