Memory Management¶
Maximum allocation size and alignment¶
We've seen details about the Size and Object_Size attributes in
the section about
data representation.
Later on, we also mentioned the
Storage_Size attribute.
In this section, we expand our discussion on sizes and talk about the
Max_Size_In_Storage_Elements and the Max_Alignment_For_Allocation
attributes. These attributes return values that are important in the allocation
of memory subpools via the Allocate
procedure from the System.Storage_Pools and the
System.Storage_Pools.Subpools packages:
procedure Allocate(
Pool : in out Root_Storage_Pool;
Storage_Address : out Address;
Size_In_Storage_Elements : Storage_Elements.Storage_Count;
Alignment : Storage_Elements.Storage_Count);
procedure Allocate (
Pool : in out Root_Storage_Pool_With_Subpools;
Storage_Address : out Address;
Size_In_Storage_Elements : Storage_Elements.Storage_Count;
Alignment : Storage_Elements.Storage_Count);
In fact, the Max_Size_In_Storage_Elements attribute indicates the
maximum value that can be used for the actual Size_In_Storage_Elements
parameter of the Allocate procedure . Likewise, the
Max_Alignment_For_Allocation attribute indicates the maximum value for
the actual Alignment parameter of the Allocate procedure. (We
discuss more details about this procedure later on.)
The Allocate procedure is called when we allocate memory for access
types. Therefore, the value returned by the Max_Size_In_Storage_Elements
attribute for a subtype S indicates the maximum value of storage
elements when allocating memory for an access type whose designated subtype is
S, while the Max_Alignment_For_Allocation attribute indicates the
maximum alignment that we can use when we allocate memory via the new
allocator.
Relevant topics
Code example with scalar type¶
Let's see a simple type T and two types based on it — an array and
an access type:
package Custom_Types is
type T is new Integer;
type T_Array is
array (Positive range <>) of T;
type T_Access is access T;
end Custom_Types;
The test procedure Show_Sizes shows the values returned by the
Size, Max_Size_In_Storage_Elements, and
Max_Alignment_For_Allocation attributes for the T type:
with Ada.Text_IO; use Ada.Text_IO;
with System;
with Custom_Types; use Custom_Types;
procedure Show_Sizes is
begin
Put_Line
("T'Size: "
& Integer'Image
(T'Size
/ System.Storage_Unit)
& " storage elements ("
& T'Size'Image
& " bits)");
Put_Line
("T'Max_Size_In_Storage_Elements: "
& T'Max_Size_In_Storage_Elements'Image
& " storage elements ("
& Integer'Image
(T'Max_Size_In_Storage_Elements
* System.Storage_Unit)
& " bits)");
Put_Line
("T'Max_Alignment_For_Allocation: "
& T'Max_Alignment_For_Allocation'Image
& " storage elements ("
& Integer'Image
(T'Max_Alignment_For_Allocation
* System.Storage_Unit)
& " bits)");
end Show_Sizes;
On a typical desktop PC, you might get 4 storage elements (corresponding to 32 bits) as the value returned by these attributes.
In the original implementation of the Custom_Types package, we allowed
the compiler to select the size of type T. We can be more specific in
the type declarations and use the Size aspect for that type:
package Custom_Types is
type T is new Integer
with Size => 48;
type T_Array is
array (Positive range <>) of T;
type T_Access is access T;
end Custom_Types;
Let's see how this change affects the Size,
Max_Size_In_Storage_Elements, and Max_Alignment_For_Allocation
attributes:
with Ada.Text_IO; use Ada.Text_IO;
with System;
with Custom_Types; use Custom_Types;
procedure Show_Sizes is
begin
Put_Line
("T'Size: "
& Integer'Image
(T'Size
/ System.Storage_Unit)
& " storage elements ("
& T'Size'Image
& " bits)");
Put_Line
("T'Max_Size_In_Storage_Elements: "
& T'Max_Size_In_Storage_Elements'Image
& " storage elements ("
& Integer'Image
(T'Max_Size_In_Storage_Elements
* System.Storage_Unit)
& " bits)");
Put_Line
("T'Max_Alignment_For_Allocation: "
& T'Max_Alignment_For_Allocation'Image
& " storage elements ("
& Integer'Image
(T'Max_Alignment_For_Allocation
* System.Storage_Unit)
& " bits)");
end Show_Sizes;
If the code compiles, you should see that T'Size now corresponds to 6
storage elements (i.e. 48 bits). On a typical desktop PC, the value of
T'Max_Size_In_Storage_Elements and T'Max_Alignment_For_Allocation
should have increased to 8 storage elements (64 bits).
Code example with array type¶
Note that using the Size and Max_Size_In_Storage_Elements
attributes on array types can give you a potentially higher number:
with Ada.Text_IO; use Ada.Text_IO;
with System;
with Custom_Types; use Custom_Types;
procedure Show_Sizes is
begin
Put_Line
("T_Array'Max_Size_In_Storage_Elements: "
& T_Array'Max_Size_In_Storage_Elements'Image
& " storage elements ("
& Long_Integer'Image
(T_Array'Max_Size_In_Storage_Elements
* System.Storage_Unit)
& " bits)");
Put_Line
("T_Array'Max_Alignment_For_Allocation: "
& T_Array'Max_Alignment_For_Allocation'Image
& " storage elements ("
& Integer'Image
(T_Array'Max_Alignment_For_Allocation
* System.Storage_Unit)
& " bits)");
Put_Line
("T_Array'Size: "
& Long_Integer'Image
(T_Array'Size
/ System.Storage_Unit)
& " storage elements ("
& T_Array'Size'Image
& " bits)");
end Show_Sizes;
In this case, these values indicate the maximum amount of memory that is theoretically available for the array in the memory pool. This information allows us to calculate the (theoretical) maximum number of components for an array of this type:
with Ada.Text_IO; use Ada.Text_IO;
with System;
with Custom_Types; use Custom_Types;
procedure Show_Sizes is
begin
Put_Line
("T_Array: Max. number of components: "
& Long_Integer'Image
(T_Array'Max_Size_In_Storage_Elements /
(T'Size
/ System.Storage_Unit))
& " components");
end Show_Sizes;
By dividing the value returned by the Max_Size_In_Storage_Elements
attribute with the size of each individual component, we can get the maximum
number of components.
Storage elements¶
We saw parts of the System.Storage_Elements package while discussing
addresses. However, we haven't discussed yet
the main types from that package: Storage_Element and
Storage_Array.
We defined storage elements previously.
In the System.Storage_Elements package, a storage element is represented
by the Storage_Element type. Its size (Storage_Element'Size) is
equal to Storage_Unit — which we also mentioned previously.
The Storage_Array type is an array type of storage elements. This is its
definition:
type Storage_Array is
array (Storage_Offset range <>) of
aliased Storage_Element;
A storage array is used to represent a contiguous sequence of storage elements
in memory. In other words, you can think of an object of Storage_Array
type as a (memory) buffer.
Important
Note that arrays of Storage_Array type are guaranteed by the
language to be contiguous. In contrast, storage pools are not required to
be contiguous blocks of memory. However, each memory allocation in a
storage pool returns a pointer to a contiguous block of memory.
Also, arrays in general are not guaranteed to be contiguous — apart
from arrays of Storage_Array type, as we've just seen. In practice,
however, if you're using a modern architecture, you most likely won't
encounter an array that isn't allocated on a contiguous block. (You would
perhaps see an array allocated on non-contiguous blocks when using an older
architecture with segmented memory.)
For further reading
Note that the Storage_Offset is an integer type with a range defined
by the compiler implementation. It's used not only
in the definition of the Storage_Array but also in
address arithmetic, which we discussed
in an earlier chapter.
In fact, the Storage_Array is used in the generic Storage_IO
package to define a memory buffer:
with System.Storage_Elements;
use System.Storage_Elements;
subtype Buffer_Type is
Storage_Array (1 .. Buffer_Size);
Let's see a simple example that makes use of the Storage_IO package:
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Storage_IO;
procedure Show_Storage_IO is
type Rec is record
A, B : Integer;
C : Float;
end record;
package Rec_Storage_IO is new
Ada.Storage_IO (Element_Type => Rec);
use Rec_Storage_IO;
Buf : Buffer_Type;
R1, R2 : Rec;
begin
R1 := (1, 2, 3.0);
Put_Line ("R1 : " & R1'Image);
-- Writing from R1 to the buffer Buf:
Write (Buf, R1);
-- Reading from the buffer Buf to R2:
Read (Buf, R2);
Put_Line ("R2 : " & R2'Image);
end Show_Storage_IO;
In this example, we instantiate the Storage_IO package for the
Rec type and declare a buffer Buf of Buffer_Type type.
(Note that Buf is essentially an array of Storage_Array type.)
We then use this buffer and write an element to it (via Write) and read
from it (via Read).
Memory pools¶
Relevant topics
Todo
Complete section!
Memory subpools¶
Relevant topics
Todo
Complete section!
Secondary stack¶
Relevant topics
GNAT-specific secondary stack
Todo
Complete section!