Опубликован: 23.07.2006 | Доступ: свободный | Студентов: 2213 / 889 | Оценка: 4.28 / 4.17 | Длительность: 21:37:00
Специальности: Системный архитектор
Лекция 10:

Управление памятью и сборка мусора

Статическое управление памятью

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

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

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

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

Пример статического распределения памяти


Рассмотрим статическое управление памятью на небольшом примере на Фортране. В этом языке вся память может быть выделена статически и во время выполнения программы будут меняться только значения простых переменных и элементы массива. Для этого каждая функция транслируется в статически выделенную область памяти, которая содержит сам код и связанные с ним данные, а связь между подпрограммами осуществляется через блоки данных, общие для нескольких подпрограмм ( COMMON ) или путем передачи параметров и передачи управления при нерекурсивных вызовах. Типы данных могут быть только одного из пяти заранее заданных видов, а переменные никогда не освобождаются.

Все это позволяет прибегнуть к максимально простому способу представления данных в памяти - одномерному массиву переменных. При этом главная функция компилируется независимо от функции SUM и потому для них создаются два различных адресных пространства. Странслированные подпрограммы объединяются уже только во время загрузки.

Любопытно, что в Фортране зафиксирована даже схема представления в памяти многомерных массивов, причем порядок записи в каком-то смысле уникален, так как требует хранить двумерные массивы по столбцам, а не по строкам. Другая особенность Фортрана более неприятна: из-за того, что во время выполнения не производятся никакие проверки, могут быть пропущены серьезные ошибки (скажем, выход за границы массива или извлечение вещественного значения в целую переменную, наложенную на ту же память с помощью оператора EQUIVALENCE ).

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

Стековое управление памятью

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

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

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

Таким образом, по сравнению со статическим распределением памяти нам удалось снять ограничения на вложенные процедуры и рекурсивные вызовы, но мы по-прежнему требуем от всех переменных фиксированных размеров. Зато при выходе из процедуры компилятор точно знает размер освободившегося блока (следовательно, сколько ячеек памяти надо снять с вершины стека). Языками, пригодными для стекового управления памятью, являются Паскаль, Алгол 68 и Модула 2.