Динамически распределяемая память

Автор работы: Пользователь скрыл имя, 26 Октября 2010 в 22:57, Не определен

Описание работы

Реферат

Файлы: 1 файл

Динамически распределяемая память.doc

— 265.50 Кб (Скачать файл)

Повреждение связанного списка блоков памяти. Эта проблема не проявится до попытки выделения  или освобождения блока.

Оба потока делят  один и тот же блок памяти. Оба  записывают в него свою информацию Когда поток 1 начнет просматривать содержимое блока, он не поймет данные, записанные потоком 2.

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

Решение этих проблем  — предоставить одному из потоков  монопольный доступ к куче и ее связанному списку (пока он не закончит все необходимые операции с кучей) Именно так и происходит в отсутствие флага HEAP_NO_SERIALIZE Этот флаг можно использовать без опаски только при выполнении следующих условий'

в процессе существует лишь один поток;

в процессе несколько  потоков, но с кучей работает лишь один из них;

в процессе несколько  потоков, но он сам регулирует доступ потоков к кучс, применяя различные  формы взаимоисключения, например критические сек ции, объекты-мьютексы или семафоры (см. главы 8 и 9).  

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

Другой флаг, HEAP_GENERATE_EXCEPTIONS, заставляет систему генерировать ис ключение при любом провале  попытки выделения блока в  куче Исключение (см. гла вы 23, 24 и 25) — еще один способ уведомления программы об ошибке. Иногда при ложение удобнее разрабатывать, полагаясь на перехват исключений, а не на провер ку значений, возвращаемых функциями.  

Второй параметр функции HeapCreate — dwlnilialSize — определяет количество байтов, первоначально передаваемых куче. При необходимости функция округляет это значение до ближайшей большей величины, кратной размеру страниц И после дний параметр, clwMaximumSize, указывает максимальный объем, до которого может расширяться куча (предельный объем адресного пространства, резервируемого под кучу). Если он больше 0, Вы создадите кучу именно такого размера и не сможете его увеличить. А если этот параметр равен 0, система резервирует регион и, если надо, расширяет его до максимально возможного объема При успешном создании кучи HeapCreate возвращает описатель, идентифицирующий новую кучу Он используется и другими функциями, работающими с кучами.  

Выделение блока  памяти из кучи  

Для этого достаточно вызвать функцию HeapAlloc  

     PVOID HeapAlloc( HANDLE hHeap, DWORD fdwFlags, SIZE_T dwBytes);  

Параметр hHeap идентифицирует описатель кучи, из которой выделяется память. Параметр dwBytes определяет число  выделяемых в куче байтов, а параметр fdwFlags позволяет указывать флаги, влияющие на характер выделения памяти В настоящее время поддерживается только три флага. HEAP_ZERO_MEMORY, HEAP_GENERATE_EX CEPTIONS и HEAP_NO_SERIALIZE.  

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

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

Идентификатор Описание
STATUS_NO_MEMORY Попытка выделения  памяти не удалась из-за ее нехватки
STATUS_ACCESS_VIOLATION Попытка выделения  памяти не удалась из-зa повреждения  кучи или неверных параметров функции
 
 

При успешном выделении  блока HeapAlloc возвращает егo адрес Если памяти не достаточно и флаг HEAP_GENERATE_EXCEPTIONS не указан, функция возвращает NULL.  

Флаг HEAP_NO_SERIALIZE заставляет HeapAlloc при данном вызове нс применять принцип последовательного  доступа к куче Этим флагом нужно  пользоваться с ве личайшей осторожностью, так как куча (особенно стандартная  куча процесса) может быть повреждена при одновременном доступе к ней нескольких потоков  

WINDOWS 98

Вызов HeapAlloc с  требованием выделить блок размером более 256 Мб Win dows 98 считает ошибкой. Заметьте, что в этом случае функция  всегда возвра щает NULL, a исключение никогда  не генерируется, даже если при создании кучи или попытке выделить блок Вы указали флаг HEAP_GENERATE_EXCEPTIONS,  

NOTE:

Для выделения  больших блоков памяти (от 1 Мб) рекомендуется  использовать функцию VirtualAlloc, а не функции, оперирующие с кучами.  

Изменение размера  блока  

Часто бывает необходимо изменить размер блока памяти. Некоторые  приложения изначально выделяют больший, чем нужно, блок, а затем, разместив  в нем данные, уменьшают его  Но некоторые, наоборот, сначала выделяют небольшой блок памяти и потом  увеличивают его по мере записи новых данных. Для изменения размера бло ка памяти вызывается функция HeapReAlloc:  

    PVOID HeapReAlloc( HANDLE hHeap, DWORD fdwFlags, PVOID pvMem, SIZE_Т dwBytes);  

Как всегда, параметр hHeap идентифицирует кучу, в которой  содержится изменя емый блок. Параметр fdwFlags указывает флаги, используемые при изменении разме  

pa блока HEAP_GENERATE_EXCEPTIONS, HEAP_NO_SERIALIZE, HEAP_ZEROMEMORY или HEAP_REALLOC_IN_PLACE_ONLY.  

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

Флаг HEAP_REALLOC_IN_PLACE_ONLY сообщает HeapReAlloc, что данный блок памяти перемещать внутри кучи не разрешается (а именно это и может попытаться сделать функция при расширении блока). Если функция сможет расширить блок без его перемещения, она расширит его и вернет исходный адрес блока. С другой сторо ны, ссли для расширения блока его надо переместить, оня возвращает адрес нового, большего по размеру блока. Если блок затем снова уменьшается, функция вновь воз вращает исходный адрес первоначального блока. Флаг HEAP_REALLOC_IN_PLACE_ ONLY имеет смысл указывать, когдя блок является частью связанного списка или де рева. В этом случае в других узлах списка или дерева могут содержаться указатели на данный узел, и его перемещение в куче непременно приведет к нарушению целост ности связанного списка  

Остальные два параметра (pvMem и dwBytes) определяют текущий адрес изменяе мого блока и сго новый размер (в байтах). Функция HeapReAlloc возвращает либо ад рес нового, измененного блока, либо NULL, ссли размер блока изменить не удалось.  

Определение размера блока  

Выделив блок памяти, можно вызвать HeapSize и узнать сго  истинный размер  

     SIZE_T HeapSize( HANDLE hHeap, DWORD fdwFlags, LPCVOlD pvMem);  

Параметр hHeap идентифицирует кучу, а параметр pvMem сообщает адрес  блока. Параметр dfwFlags принимает два  значения: 0 или HEAP_NO_SERIALIZE

Освобождение  блока  

Для этого служит функция HeapFree.  

     BOOL HeapFree( HANDLE hHeap, DWORD fdwFlags, PVOID pvMem);  

Она освобождает  блок памяти и при успешном вызове возвращает TRUE. Параметр fdwFlags принимает  два значения 0 или HEAP_NO_SFRIALIZE Обращение к этой фун кции может привести к тому, что диспетчер, управляющий кучами, вернет часть фи зической памяти системе, но это не обязательно.

Уничтожение кучи  

Кучу можно  уничтожить вызовом HeapDestroy-.  

     BOOL HeapDestroy(HANDLE hHeap);  

Обращение к  этой функции приводит к освобождению всех блоков памяти внут ри кучи и возврату системе физической памяти и зарезервированного региона адрес  

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

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

Использование куч в программах на С++  

Чтобы в поЛной мере использовать преимущества динамически распределяемой па мяти, следует включить ее поддержку в существующие программы, написанные па С++. В этом языке выделение памяти для объекта класса выполняется вызовом оператора new, а не функцией malloc, как в обычной библиотеке С. Когда необходимость в дан ном объекте класса отпадает, вместо библиотечной С-функции frее следует применять оператор delete. Скажем, у нас есть класс CSomeClasb, и мы хотим создать экземпляр этого класса. Для этого нужно написать что-то вроде.  

     CSomeClass* pSorneClass = new CSomeClass;  

Дойдя до этой строки, компиляюр С++ сначала проверит, содержит ли класс CSomeClass функцию-член, переопределяющую оператор new. Если да, компилятор генерирует код для вызова этой функции Нет  — создает код для вызова стандартно го С++-оператора new.  

Созданный объект уничтожается обращением к оператору delete  

     delete pSomeClass;  

Переопределяя операторы new и delete для нашегоC++ - класса, мы получаем воз можность использовать преимущества функций, управляющих  кучами. Для этого оп ределим класс CSomeClass в заголовочном файле, скажем, так:  

     class CSomeClass

     {  

         private  

           static HANDLE s_hHeap;

           static UINT s_uNumAllocsInHeap;  

           // здесь располагаются закрытые  данные и функции-члены  

         public:  

           void* operator new (size_t size);

           void operator delete (void* p);  

           // здесь располагаются открытые  данные и функции-члены  

    ...  

     };  

Я объявил два  элемента данных, s_hHeap и s_uNumAllocs!nHeap, как  статические переменные А раз  так, то компилятор С++ заставит все  экземпляры класса CSomeClass использовать одни и те же переменные. Иначе говоря, он не станет выделять отдель ные переменные s_hHeap и s_uNumAllocsInHeap для каждого создаваемого экземпля ра класса. Это очень важно: ведь мы хотим, чтобы все экземпляры класса CSomeClass были созданы в одной куче.  

Переменная s_hHeap будет содержать описатель кучи, в которой создаются объек  ты CSomeClass. Переменная s_uNumAllocsInHeap — просто счетчик созданных в куче  

объектов CSomeClass. Она увеличивается на 1 при создании в куче нового объекта CSomeClass и соответственно уменьшается при его уничтожении. Когда счетчик об нуляется, куча освобождается. Для управления кучей в СРР-файл следует включить примерно такой код:  

     HANDLE CSomeClass::s_hHeap = NULL;

     UINT CSomeClass::s_uNumAllocsInHeap = 0;  

     void* CSomnClass::operator new (size_t size)

     {  

         if (s_hHeap == NULL)

         {  

           // куча не существует, создаем ее 

           s_hHeap = HeapCreate(HEAP_NO_SERIALIZE, 0, 0);  

        if (s_hHeap == NULL)  

             return(NULL);  

         }  

         // куча для объектов CSomeClass существует

Информация о работе Динамически распределяемая память