Управление памятью в операционной системе WINDOWS, страница 9

p=getbuffer();

// now backwards for variety

for (int j=4000000-1;j>=0;j--)   putbuffer(p, j, j&0xFF);

freebuffer(p);

}


Использование нескольких пулов свободной памяти

Функция VirtualAlloc безусловно является мощным средством управления использования памяти, однако во многих случаях применять ее неудобно, так как она является слишком низкоуровневой. Для программистов, не желающих использовать VirtualAlloc, операционная система Windows предлагает использовать традиционные средства работы с памятью, такие как mallос и new. Эти два оператора являются стандартными механизмами выделения памяти в С и C++.

Однако в некоторых случаях использование mallос и new оказывается недостаточно эффективным. Дело в том, что оба этих оператора выделяют память из общего пула свободной памяти, который называют «кучей> (heap). Если ваша программа активно использует динамически выделяемую память, куча подвергается фрагментации. Эта проблема возникает в случае, если программа выделяет намять небольшими участками, а затем освобождает некоторые из них. В результате вся свободная память оказывается разбитой на множество несвязанных кусков. Если требуется выделить один большой непрерывный блок памяти, система либо выполняет реорганизацию используемых участков памяти (что требует дополнительного времени), либо увеличивает размер кучи (что приводит к нежелательному расходу ресурсов).

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

Для работы с несколькими кучами свободной памяти в Windows используется вызовы, перечисленные в табл. 2. Каждый процесс обладает традиционно кучей, которая используется операторами mallос и new. Дескриптор этой кучи можно узнать при помощи функции GetProcessHeap. Однако помимо этой традиционной кучи вы можете создать любое количество дополнительных пулов свободной памяти (для этого служит вызов HeapCreate).

При создании новой кучи в качестве одного из аргументов вызова HeapCreateуказывается максимальный размер кучи. Если этот аргумент имеет значение 0, значит, куча сможет увеличиваться без ограничений. Куча фиксированного размера обладает ограничением на размер каждого из элементов (размер каждого из элементов не может превышать 512 Кбайт).

Получив дескриптор кучи, можно выделить в ней участок памяти при помощи функции НеарAllос. Освободить память в куче можно при помощи функции HeapFree. Доступ к куче можно заблокировать для всех потоков, кроме одного. Обычно это осуществляется автоматически при обращении одного из потоков к куче, однако вы можете сделать это самостоятельно перед началом серии операций с кучей. Таким способом можно улучшить производительность программы.

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

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