Справочник Ada 2005
13.11 Управление памятью
Каждый тип доступа к объекту имеет связанный пул памяти. Память, выделенная аллокатором, берется из пула; экземпляры Unchecked_Deallocation возвращают память в пул. Несколько типов доступа могут использовать один и тот же пул.
Пул памяти — это переменная типа, унаследованного от Root_Storage_Pool, который является абстрактным ограниченным управляемым типом. По умолчанию реализация выбирает *стандартный пул памяти* для каждого типа доступа к объекту. Пользователь может определять новые типы пулов и может переопределить выбор пула для типа доступа к объекту, указав Storage_Pool для типа.
Правила допустимости
Если для данного типа доступа указан Storage_Pool, то Storage_Size для него не должен быть указан.
Статическая семантика
Существует следующий пакет библиотеки, определенный языком:
with Ada.Finalization;
with System.Storage_Elements;
package System.Storage_Pools is
pragma Preelaborate(System.Storage_Pools);
with System.Storage_Elements;
package System.Storage_Pools is
pragma Preelaborate(System.Storage_Pools);
type Root_Storage_Pool is
abstract new Ada.Finalization.Limited_Controlled with private;
pragma Preelaborable_Initialization(Root_Storage_Pool);
abstract new Ada.Finalization.Limited_Controlled with private;
pragma Preelaborable_Initialization(Root_Storage_Pool);
procedure Allocate(
Pool : in out Root_Storage_Pool;
Storage_Address : out Address;
Size_In_Storage_Elements : in Storage_Elements.Storage_Count;
Alignment : in Storage_Elements.Storage_Count) is abstract;
Pool : in out Root_Storage_Pool;
Storage_Address : out Address;
Size_In_Storage_Elements : in Storage_Elements.Storage_Count;
Alignment : in Storage_Elements.Storage_Count) is abstract;
procedure Deallocate(
Pool : in out Root_Storage_Pool;
Storage_Address : in Address;
Size_In_Storage_Elements : in Storage_Elements.Storage_Count;
Alignment : in Storage_Elements.Storage_Count) is abstract;
Pool : in out Root_Storage_Pool;
Storage_Address : in Address;
Size_In_Storage_Elements : in Storage_Elements.Storage_Count;
Alignment : in Storage_Elements.Storage_Count) is abstract;
function Storage_Size(Pool : Root_Storage_Pool)
return Storage_Elements.Storage_Count is abstract;
return Storage_Elements.Storage_Count is abstract;
private
... -- не задано языком
end System.Storage_Pools;
... -- не задано языком
end System.Storage_Pools;
Тип пула памяти (или тип пула) является потомком Root_Storage_Pool. Элементы пула памяти — это объекты, выделенные в пуле аллокаторами.
Для каждого подтипа S доступа к объекту определены следующие атрибуты представления:
S'Storage_Pool
Обозначает пул памяти типа S. Тип этого атрибута — Root_Storage_Pool'Class.
S'Storage_Size
Возвращает результат вызова Storage_Size(S'Storage_Pool), который предназначен для измерения количества элементов памяти, зарезервированных для пула. Тип этого атрибута — universal_integer.
Storage_Size или Storage_Pool могут быть заданы для типа доступа к объекту, не являющегося производным, с помощью описания_атрибута; имя в инструкции Storage_Pool должно обозначать переменную.
Аллокатор типа T выделяет память из пула памяти T. Если пул памяти — это пользовательский объект, то память выделяется путем вызова Allocate, передавая T'Storage_Pool в качестве параметра Pool. Параметр Size_In_Storage_Elements указывает количество элементов памяти, подлежащих выделению, и не превышает D'Max_Size_In_Storage_Elements, где D — назначенный подтип. Параметр Alignment — D'Alignment. Результат, возвращаемый в параметре Storage_Address, используется аллокатором в качестве адреса выделенной памяти, которая представляет собой непрерывный блок памяти размером Size_In_Storage_Elements элементов памяти. Любое исключение, распространяемое процедурой Allocate, распространяется аллокатором.
Если Storage_Pool не указан для типа, определенного определением_доступа_к_объекту, то реализация выбирает для него стандартный пул памяти по своему усмотрению. В этом случае аллокатор генерирует исключение Storage_Error, если памяти недостаточно. Определяется реализацией, предоставляет ли она имена стандартного типа пула(ов), доступные пользователю.
Если Storage_Size указан для типа доступа, то Storage_Size этого пула не меньше запрошенного, а память пула возвращается, когда модуль, содержащий объявление типа доступа, покидает область видимости.
Если реализация не может удовлетворить запросу, Storage_Error генерируется в точке описания_атрибута. Если ни Storage_Pool, ни Storage_Size не указаны, то значение Storage_Size определяется реализацией.
Если для типа доступа задан Storage_Pool, то используется заданный пул.
Эффект вызова Allocate и Deallocate для стандартного пула памяти напрямую (вместо неявного вызова через аллокатор или экземпляр Unchecked_Deallocation) не определён.
Ошибочное выполнение
Если Storage_Pool указан для типа доступа, то если Allocate может удовлетворить запрос, оно должно выделить непрерывный блок памяти и вернуть адрес первого элемента памяти в Storage_Address. Блок должен содержать Size_In_Storage_Elements элементов памяти и должен быть выровнен в соответствии с Alignment. Выделенная память не должна использоваться для других целей, пока элемент пула существует. Если запрос не может быть удовлетворён, Allocate должно распространить исключение (например, Storage_Error). Если Allocate ведет себя каким-либо другим образом, выполнение программы является ошибочным.
Требования к документации
Реализация должна документировать набор значений, которые должна принимать процедура Allocate пользователя для параметра Alignment. Реализация должна документировать, как выбирается стандартный пул памяти, и как выделяется память стандартными пулами памяти.
Рекомендации по реализации
Реализация должна документировать случаи, в которых она динамически выделяет память кучи для целей, отличных от оценки аллокатора.
Стандартный (предоставленный реализацией) пул памяти для типа доступа к константе не должен иметь накладных расходов для поддержки освобождения отдельных объектов.
Пул памяти, используемый для аллокатора анонимного типа доступа, определяется следующим образом:
- Если аллокатор определяет сорасширение (см. 3.10.2) объекта, создаваемого внешним аллокатором, то пул памяти, используемый для внешнего аллокатора, также должен использоваться для сорасширения;
- Для других дискриминаторов доступа и параметров доступа пул памяти должен создаваться в момент аллокации и освобождаться, когда выделенный объект становится недоступным;
- В противном случае, стандартный пул памяти должен создаваться в момент разработки анонимного типа доступа; такой пул памяти не обязан поддерживать освобождение отдельных объектов.
ПРИМЕЧАНИЯ
24 Пользовательский тип пула памяти может быть получен путем расширения типа Root_Storage_Pool и переопределения примитивных подпрограмм Allocate, Deallocate и Storage_Size. Затем пользовательский пул памяти можно получить, объявив объект типа расширения.
25 Автор определенных пользователем процедур выделения и освобождения памяти и пользователи аллокаторов для связанного типа доступа несут ответственность за взаимодействие с задачами. В частности:
- Если аллокаторы используются в разных задачах, они требуют взаимного исключения.
- Если они используются внутри защищенных объектов, они не могут блокироваться.
- Если они используются обработчиками прерываний (см. C.3, «Поддержка прерываний»), механизм взаимного исключения должен работать должным образом в этом контексте.
26 Примитивы Allocate, Deallocate и Storage_Size объявлены абстрактными (см. 3.9.3), и поэтому они должны быть переопределены при объявлении нового (не абстрактного) типа пула памяти.
Примеры
Чтобы связать тип доступа с объектом пула памяти, пользователь сначала объявляет объект пула некоторого типа, производного от Root_Storage_Pool. Затем пользователь определяет его атрибут Storage_Pool, как показано ниже:
Pool_Object : Some_Storage_Pool_Type;
type T is access Designated;
for T'Storage_Pool use Pool_Object;
END_OF_DOCUMENT_MARKER for T'Storage_Pool use Pool_Object;
Другой тип доступа может быть добавлен к существующему пулу памяти, через:
for T2'Storage_Pool use T'Storage_Pool;
Семантика этого определяется реализацией для стандартного пула памяти.
Как обычно, производный от Root_Storage_Pool может определять дополнительные операции. Например, предполагая, что Mark_Release_Pool_Type имеет две дополнительные операции, Mark и Release, следующее является возможным использованием:
type Mark_Release_Pool_Type
(Pool_Size : Storage_Elements.Storage_Count;
Block_Size : Storage_Elements.Storage_Count)
is new Root_Storage_Pool with private;
(Pool_Size : Storage_Elements.Storage_Count;
Block_Size : Storage_Elements.Storage_Count)
is new Root_Storage_Pool with private;
...
MR_Pool : Mark_Release_Pool_Type (Pool_Size => 2000,
Block_Size => 100);
Block_Size => 100);
type Acc is access ...;
for Acc'Storage_Pool use MR_Pool;
...
for Acc'Storage_Pool use MR_Pool;
...
Mark(MR_Pool);
... --Выделить объекты, используя «new Designated(...)».
Release(MR_Pool); --Освободить память.
... --Выделить объекты, используя «new Designated(...)».
Release(MR_Pool); --Освободить память.