А.В. Ахо, М.С. Лам, Р. Сети, Дж. Д. Ульман - Компиляторы - принципы, технологии и инструментарий (1114947), страница 114
Текст из файла (страница 114)
В этом случае нет дополнительных расходов памяти на организацию списка, хотя при этом его наличие определяет нижнюю границу размера свободного блока; такой блок должен содержать два дескриптора границ и два указателя, даже если обьект состоит из одного байта. Порядок блоков в списке остается неопределенным. Например, список может быть отсортироваи по размеру, что облегчает использование стратегии наилучшего подходящего. Пример 7.10. На рис. 7.17 показана часть кучи с тремя смежными блоками А, В и С. Блок В размером 100 только что освобожден и возвращен в список свободных блоков. Поскольку известно начало (левый конец) блока В, также извес~си конец блока, непосредственно примыкающего к В слева, т.е. блока А в нашем примере.
Бит использования блока на правом конце А равен О, т.е. блок А также свободен. Следовательно, блоки А и В можно слить в один блок длиной 300 байт. Блок А Блок В Блок С .',о вю, Рис. 7.17. Часть кучи и дважды связанный список свободных блоков Может оказаться, что блок С, примыкающий к В справа, также свободен; в этом случае можно объединить все три блока — А„В и С. Заметим, что если при освобождении блока всегда выполняется его слияние, то в ходе этого процесса не надо просматривать иные блоки, кроме двух смежных с освобождаемым. В нашем случае найти блок С можно, зная левый конец блока В и общее количество байтов в блоке (известное из дескриптора границы). После этого можно проверить, свободен ли блок С (рассматривая соответствующий бит в его дескрипторе границы).
В нашем случае этот бит равен 1, так что блок С занят и не может быть слит с блоком В. 565 7.4. Управление кучей Поскольку мы сливаем блоки А и В, нам надо удалить один из них из списка свободных блоков. Структура дважды связанного списка позволяет легко определить блоки перед и после А и В. Заметим, что "физические" соседи А и В не обязаны быть соседями в списке свободных блоков. Зная предшествующие и последующие блоки А и В, легко выполнить необходимгяе действия с указателями для замещения .4 н В одним объединенным свободным блоком. и Автоматическая сборка мусора может устранить фрагментацию путем перемещения всех выделенных объектов в один непрерывный блок. Взаимодействие сборки мусора и управления памятью более подробно рассматривается в разделе 7.6.4.
7.4.5 Освобождение памяти вручную Завершим этот раздел управлением памятью вручную, ко~да программист должен явно освободить используемую память, как это делается в языках программирования С и Сьь. В идеальном случае любая память, которая больше не нужна, должна быть освобождена. И наоборот, любая память, к которой может быть выполнено обращение, не должна освобождаться.
К сожалению, и то, и другое трудно выполнить. Наряду с рассмотрением трудностей ручного освобождения памяти будут описаны и некоторые методы их преодоления. Проблемы освобождения памяти вручную Освобождение памяти вручную может приводить к ошибкам. Наиболее распространены ошибки двух видов: "неудаление" данных, обращение к которым более невозможно, называемое утечкой памяти, и обращение к удаленным данным, называемое ошибкой разыменования висящего указателя. Программистам трудно сказать, действительно ли программа в будущем никогда не обратится к некоторым данным, так что первая распространенная ошибка состоит в том, что не освобождается память, к которой больше не будет обращений.
Заметим, что хотя утечки памяти могут замедлить выполнение программы из-за возрастания количества требующейся ей памяти, онн не влияют на корректность программы„пока память не окажется исчерпанной. Многие программы могут выдержать утечки памяти, особенно если они невелики. Однако для программ с большим временем работы, в особенности для программ, работающих безостановочно, наподобие операционных систем или серверного кода, отсутствие утечек становится критичным.
Автоматическая сборка мусора позволяет избавиться от утечек путем освобождения более не используемой памяти. Но даже в этом случае программа может использовать памяти больше, чем это необходимо. Программист может знать, что обращений к объекту больше не будет, несмотря на наличие в каком-то месте про- 566 Глава 7.
Среды времени выполнения граммы ссылки на него. В таком случае программист обязан удалить эту ссылку, чтобы объект мог быть удален автоматически. Слишком усердно удаляя объекты, можно вызвать еще большие проблемы. Вторая распространенная ошибка состоит в удалении объекта, к которому позже пытается обратиться программа.
Указатель на освобожденную память известен под названием висящий указатель (бапяйпя ро1пгег). После того, как освобожденная память вновь выделена новому объекту, операции чтения, записи и освобождения памяти с использованием висящего указателя могут привести к самым непредсказуемым результатам. Освобождение памяти с использованием висящего указателя означает, что память новой переменной может использоваться для размещения еще одной переменной, и обращения к старой и новой переменным будут конфликтовать друг с другом. В отличие от утечки памяти разыменование висящего указателя после того, как освобожденная память выделена заново, почти всегда приводит к ошибке в программе, которую к тому же очень сложно отладить. В результате программисты не спешат удалять объекты, пока не становятся абсолютно уверены, что они больше не потребуются.
Еще одна ошибка на ту же тему — доступ по некорректному адресу. Распространенными примерами этой ошибки являются разыменование нулевого указателя и обращение к элементам за границей массива. Лучше, если такие ошибки проявляются сразу, чем если они тихонько портят результат работы программы. В действительности многие нарушения защиты используют такие программные ошибки, когда некоторая программа допускает непредусмотренное обращение к данным, что позволяет хакерам получить управление программой и машиной.
Противоядием для этого являются проверки, вносимые компилятором для каждого обращения к памяти, чтобы убедиться в том, что все обращения происходят внутри разрешенных границ. Оптимизатор может обнаружить и удалить те проверки, для которых он в состоянии убедиться, что все обращения будут выполняться в пределах границ, так что эти проверки реально не нужны. Программные соглашения и инструментарий Теперь мы представим несколько популярных соглашений и инструментов, разработанных для того, чтобы помочь программистам в нелегком деле управления памятью.
° Владение объектом (оЬ)ес1 овпегз)пр) полезно, если вопрос о времени жизни объекта может быть решен статически. Идея заключается в том, чтобы на все время жизни связать с каждым объектом его владельца. Владелец представляет собой указатель на объект, предположительно принадлежащий некоторому вызову функции. Владелец (т.е. его функция) отвечает за удаление объекта либо передачу его другому владельцу. Можно иметь 5б7 7.4. Управление кучей Пример: РигЫу Ропоту — один из многих популярных коммерческих инструментов, которые помогают программистам обнаруживать ошибки обращения к памяти и утечки памяти в программах. Ропоту добавляет к бинарному коду дополнительные команды для выявления ошибок во время работы программы.
Этот инструмент отслеживает карту памяти с указанием свободных и занятых блоков. Каждый объект в памяти окружен дополнительной памятью. Обращение к не выделенной объекту памяти или памяти между объектами рассматривается как ошибка. Такой подход позволяет выявить ряд обращений по висящим указателям, но не в ситуации, когда объект был удален и его место занял другой объект. Данный инструментарий находит также обращения за пределы массива — если оно выполняется к дополнительной памяти, добавленной им в конце объекта. Ропоту обнаруживает и утечки памяти по окончании работы программы.
Он просматривает содержимое всех распределенных объектов в поисках возможных значений указателей. Любой обьект, на который не указывает ни один указатель, является потерянным блоком памяти. Ропоту сообшает о количестве потерянной памяти и расположении потерянных объектов. В этом плане можно сравнить Ропоту с "консервативным сборщиком мусора", который будет рассмотрен в разделе 7.8.3. и другие, не владеющие объектом, указатели на него; такие указатели могут в любой момент быть перезаписаны и их значения не должны участвовать в удалениях объектов.
Такое соглашение устраняет как утечки памяти, так и попытки многократного удаления одного и того же объекта. Однако оно не спасает от висящих указателей, поскольку возможно обращение к удаленному объекту посредством указателя, не являющегося владельцем. ° Подсчет ссылок (геГегепсе соип6пй) полезен в ситуации, когда время жизни объекта должно быть определено динамически. Идея заключается в связывании с каждым динамически выделенным объектом счетчика. При создании ссылки на объект значение счетчика ссылок увеличивается; при удалении ссылки значение счетчика уменьшается.