Мегаобучалка Главная | О нас | Обратная связь


Управление памятью в приложениях



2019-07-03 238 Обсуждений (0)
Управление памятью в приложениях 0.00 из 5.00 0 оценок




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

При разработке программ необходимо:

· правильно выбирать типы данных для описания переменных и место их размещения в программе;

· использовать динамическую память для временного размещения данных;

· использовать библиотеки динамической компоновки (dll).

 

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

В функции

void test () { float a; double b; int c[100]; }

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

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

 

double mas[100000]; void main () { } void main () { static double mas[100000]; } // ключевое слово static говорит само за себя

 

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

Динамическая память размещается в специально выделяемой области, которая носит название «куча». Для каждого процесса Windows по умолчанию резервирует одну кучу размером в 1Мбайт. Дескриптор кучи, 113

 

созданный для процесса по умолчанию, можно получить, воспользовавшись функцией GetProcessHeap(). Библиотечные функции выделения и освобождения памяти обращаются к куче, чтобы найти свободный блок достаточного размера. Выделенный блок помечается как занятый, и его адрес возвращается приложению.

При динамическом выделении памяти под данные их можно хранить в памяти и при этом не обозначать именем переменной, а использовать специальную переменную, которая называется указатель. Однако в языках С++/C# используется альтернативный тип указателя – ссылка. Поэтому свободное владение механизмом применения ссылок является одним из ключевых моментов разработки сложных и эффективных программ.

Указатель – это поименованный адрес памяти, или мнемоническое обозначение адреса. Сам адрес является положительным целым числом (типа unsigned). При динамическом выделении памяти указатель содержит адрес первого байта, с которого хранится объект в памяти. Компилятор отводит под переменную типа указатель в 32–разрядной машине 4 байта памяти независимо от того, на какой тип данных ссылается указатель и является ли сам указатель глобальной или локальной переменной.

Указатель может находиться в одном из трех состояний, а именно:

 быть не инициализированным конкретным адресом памяти;

 содержать адрес объекта;

 содержать число ноль (такой указатель называют NULL–указателем) и не ссылаться ни на какую переменную/объект.

 

Пример Си–программы с объявлением указателя и его инициализации.

# include <stdio.h>

void main ()

{ int a, *ptr = NULL; // объявляем указатель на тип int и записываем в

// переменную типа указатель ноль;

ptr = & a; // инициализируем указатель адресом переменой a;

// теперь ptr символический адрес переменной а;

printf(“Адрес переменной a = %p\n”, ptr);

}

Объявление в программе int **point означает, что переменная point используется для хранения адреса адреса. Указателю point при распределении памяти будет выделено также 4 байта. Проиллюстрируем возможность использования таких указателей следующей программой.

void main()

{

int a,*point_1,**point_2,***point_3;

point_1 = &a; // инициализируем указатель адресом переменной а

point_2 = &point_1; // это адрес адреса переменной а

point_3 = &point_2; // это адрес адреса адреса переменной а

***point_3 = 10; // присваиваем переменной а значение 10

}

В последнем операторе выполняется запись числа по адресу адреса адреса переменной а. В этом примере показана простая, двойная и тройная косвенные адресации.

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

Использование указателей совместно с операторами new и delete позволяет осуществлять выделение памяти динамически, то есть по запросу программы. Оператор new позволяет выделить область памяти для размещения величины базового типа данных, причем размер выделяемого участка памяти равен размеру того типа, с которым связан указатель. Указатель принимает значение адреса выделенной области.

Примеры:

 Выделяем память для переменной типа double и записываем в эту память число

double *ptr = new double (3.1415).

Здесь указатель ptr инициализируется адресом памяти, выделенной для хранения числа типа double. Сама переменная не имеет имени. Есть только указатель на нее.

 Выделяем память для одномерного массива типа int

int *ptr_1 = new int [100000].

Здесь указатель инициализируется адресом памяти, выделенной для хранения числа типа int. Сам маcсив не имеет имени. Есть только указатель на него.

Оператор delete позволяет освободить область памяти, на которую ссылается указатель, для последующего использования. После выполнения процедуры значение указателя становится неопределенным. Каждому оператору new должен соответствовать свой оператор delete. В противном случае будет иметь место утечка памяти!

Примеры:

 освобождаем память для переменной и сразу же устанавливаем указатель в нуль. Это позволяет избежать ошибок при повторном применении delete к указателю (к указателям, не установленным в ноль, оператор delete нельзя применять дважды).

delete ptr;

ptr =NULL; //освобождаем память, выделенную под массив

delete [] ptr_1; //Скобки [] в этом случае обязательны!

ptr_1 = NULL; // инициализируем ptr_1 нулем.

Современные языки программирования можно условно разделить на две группы. В первой группе программист ответственен за управление памятью и обязан вовремя освобождать неиспользуемые блоки ( С, С++). Вторая группа – это языки с так называемой сборкой мусора (C#, Java– оператор delete отсутствует). Системы программирования могут оптимизировать кучу, для чего они периодически выполняют её дефрагментацию – собирают свободные блоки вместе. Для этого приходится перемещать объекты, находящиеся в куче. Для языков без сборки мусора, где обращение к объекту в куче происходит через адрес, дефрагментация кучи невозможна или очень затруднительна.

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

В ОС Windows реализовать систему управления памятью «вручную», если стандартная не годится, позволяют функции HeapAlloc() и HeapFree(). В частности, для создания процессами частных куч во время их выполнения. Частная куча – это куча, созданная для размещения в памяти некоторых однотипных объектов в обход стандартных механизмов выделения памяти из общей кучи.

Использование библиотек динамической компоновки. Библиотеки динамической компоновки DLL (Dynamic Link Libraries) являются стержневым компонентом операционной системы Windows NT и многих приложений Windows. Сложные программы всегда состоят из большого числа отдельных подпрограмм. Каждая такая подпрограмма обычно хранится в отдельном файле. Если файлов много, то возникают известные неудобства для продуктивной работы над программным проектом. В этой связи основные и хорошо отлаженные подпрограммы, которые являются «строительным материалом», собирают с помощью специальной программы в отдельный библиотечный файл. Использование файлов библиотек модулей является исключительно удобным средством. Все системы программирования и инструментальные средства поставляются с готовыми библиотеками. При инсталляции таких сред обычно создается каталог и именем LIB, в котором содержатся библиотеки функции.

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

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

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

 

при необходимости, например когда какая–нибудь программа захочет вызвать библиотечную функцию.



2019-07-03 238 Обсуждений (0)
Управление памятью в приложениях 0.00 из 5.00 0 оценок









Обсуждение в статье: Управление памятью в приложениях

Обсуждений еще не было, будьте первым... ↓↓↓

Отправить сообщение

Популярное:
Почему люди поддаются рекламе?: Только не надо искать ответы в качестве или количестве рекламы...
Личность ребенка как объект и субъект в образовательной технологии: В настоящее время в России идет становление новой системы образования, ориентированного на вхождение...
Организация как механизм и форма жизни коллектива: Организация не сможет достичь поставленных целей без соответствующей внутренней...



©2015-2024 megaobuchalka.ru Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. (238)

Почему 1285321 студент выбрали МегаОбучалку...

Система поиска информации

Мобильная версия сайта

Удобная навигация

Нет шокирующей рекламы



(0.009 сек.)