PowerAda How a PowerAda Program Arranges its Memory
PowerAda programs use the standard AIX or Linux memory management. Understanding the way a PowerAda program organizes the memory it uses should help you to determine the causes of STORAGE_ERROR exceptions, system out-of-memory conditions, and problems with corrupted memory, and design your programs to avoid them.
You can customize the default or maximum size for a number of different memory areas through the ulimit or limit shell commands, compiler bind options, and runtime environment variables. The default sizes are usually only restricted by the limit for the process. If the Ada program was bound with the
-q option that controls a particular limit, the value specified will be used (as long as it is smaller than the operating system limit). If the value specified is environment_variable then PowerAda will check for the existence of the corresponding environment variable at program startup to determine the limit to be imposed.
The memory associated with an Ada program on AIX (as for any AIX program) is organized so that most all read/write portions are located in AIX segment 2. The stacks associated with an AIX thread are sometimes allocated in segment 3 (depending on the AIX version you are using). Also, data from AIX shared libraries, may reside in other AIX segments.
The main program stack is allocated by AIX near the highest address of segment 2 (near 16#2FFF_FFFF#) and the heap and read/write are allocated towards the lowest address of the segment (16#2000_0000#). The main program stack grows from high to low addresses; the heap grows from low to high addresses.
All read-only portions are located in segment 1, with executable code and constant data grouped near the lowest address of the segment (16#1000_0000#).
The heap is a general-purpose storage area managed by the malloc and free routines of the AIX or Linux operating system. Each Ada main program has its own heap. Tasks share the same heap as their main program.
The evaluation of the Ada new statement reserves some memory from the heap. The access variables in your program refer to objects that are allocated this way from the heap. UNCHECKED_DEALLOCATION operations return memory to the heap. Programs also use the heap to manage objects, such as arrays with variable bounds, whose size the compiler cannot determine at compile time; the program refers to these objects through access variables, even though these variables might not appear in your source code.
Because the Ada language requires a program to raise a STORAGE_ERROR exception when the heap runs out of space, each PowerAda program sets an upper limit on its heap size during elaboration of the main program. To determine the heap limit for programs you run, issue the command ulimit
-a (for sh or ksh) or limit (for csh). The data limit represents the number of Kbytes available for the heap and static read/write area. You can use the ulimit or limit command to reduce the data limit, thus reducing the maximum heap size.
Another area of temporary storage is the stack. This area in segment 2 is reserved by AIX. The stack holds temporary information, such as the values of local variables, for subprograms. If it fills up at run time, a PowerAda program raises a STORAGE_ERROR exception. To determine how much space is available for the stack or to set the size of the stack, you can use the same procedure as for the heap: view or change the maximum size with the ulimit or limit command, and bind with the
-qstack_limit option to make programs you run use a smaller stack than the stack limit allows. Additionally, if the
-q stack_limit value specified is environment_variable, then the environment variable Ada_stack_limit is used by PowerAda upon program startup.
Each Ada task also has a stack. Stack storage for a task is allocated by the compiler from the heap when the task is activated. Ada tasks have a default stack size of at least 32K bytes. You can override the default for all tasks by binding with the
-qtask_stack option. If the
-q task_stack value specified is environment_variable, then the environment variable Ada_task_stack is used by PowerAda for the default task stack size. You can override the default for a particular task type with a storage size clause. For example, to give all tasks of type
T 100,000 bytes of stack, you would declare the task type as:
task type T is entry E; end; for T'STORAGE_SIZE use 100_000;
Note that this size clause takes precedence over the command line option and environment variable.
Since task stacks are allocated by AIX when the AIX thread is created for the task, you can make a task stack as small as AIX will allow (see the value of PTHREAD_STACK_MIN in /usr/include/pthread.h) which is usually a minimum of 96K bytes, or as large as memory allows. Any value you specify will be rounded up to at least PTHREAD_STACK_MIN. Note the stack must be large enough to accommodate activation records and other constructs that the compiler allocates from the stack. The stack for the main program is limited by the AIX stack limit. Not all space for the stack is allocated during program start-up, because it is located in a memory area that expands as needed while a program runs.
A common cause of stack problems is inter-language communication. While an Ada program is executing a C, FORTRAN, or assembler routine, the non-Ada routine has access to the Ada main program stack or a task stack, depending on where the routine is called. If the main program stack is filled up during a call to a non-Ada routine, the operating system ends the process with a signal 9. If a task stack is filled up, the non-Ada routine might overwrite other data in the heap, leading to erroneous program execution after the routine returns to Ada.
This is data associated with package specifications and bodies that are compilation units. It stays in AIX segment 2 for the duration of your program, with a location and size that are fixed at load time.
Code and Literal Area
The executable code of your program and constant values for string and numeric literals go in the read-only segment 1. The code and literal area is not modified after the program is loaded.
The compiler collects all address constants into a single area, known as the table of contents or TOC, in segment 2. Under normal circumstances, you do not need to concern yourself with the TOC.