There are two main forms of heap in the internals of QDOS/SMS. The common heap and the user heap(s). Heaps are used by jobs when they need additional memory over and above that which they have been allocated in their dataspace parameter. For example, a job which acts as a front end for a database system might only need a few Kbytes for its own internal working variables etc, but depending upon the database it is asked to open, it might need a lot more working space to hold copies in memory. Archive used to simply allocate all the memory in the system for itself and this left nothing for any other jobs. Using the heaps, and calculating its needs at runtime is a much better way of doing things.
All heaps, common or user, are allocated in multiples of 8 bytes, so a request for 2 bytes will always be rounded up to 8 instead. The different heaps, however, have different headers - the common heap has a 16 byte header while a user heap has an 8 byte header. These overheads are added to every area allocated in whichever heap.
The common heap is available to all jobs in the system. They allocate parts of the heap for themselves using the MT_ALCHP/SMS_ACHP manager trap. As areas of the common heap are allocated and deallocated, it can lead to fragmentation, so the common heap is better utilised for larger areas of memory.
When a request for some space in the common heap is made, it is first rounded up to a multiple of 8 and an extra 16 bytes are added for the header and this is the amount of area allocated. The header is in the following format :
|Offset||Allocated Space||Free Space|
|$00||Length of this . . .||. . . block|
|$04||Address of the driver to free the block||Pointer to next free space|
|$08||Id of the job that owns this block||-1|
|$0C||Address to be set when this area of the heap is released||0|
As the common heap is used by things such as opening channels, it is best to try to avoid fragmenting it, otherwise, it could happen that although there is space in the common heap, there are no bits big enough to open a new channel, and you get an out of memory error when trying to open a file. One way to avoid this is always deallocate heap areas in the reverse order that they were allocated. In a multi tasking system, however, this isn't always feasible. The other way is to use user heaps.
As mentioned above, the common heap can become badly fragmented and a way to avoid this is to make use of user heaps. These are quite simply an area of common heap which has been allocated to the job, and then small bits of this area are utilised by the job as and when it needs them. For example, a job might decide it needs 64 KBytes of Heap when it is running, so it carries out the following procedures :
Note that there is a distinction between MT_ALLOC/SMS_ALHP and MT_LNKFR/SMS_REHP on the one hand and MM_ALLOC/MEM_ALHP and MM_LNKFR/MEM_REHP on the other. The first two, which are Traps, are atomic and, in addition, A0 and A1 are relative to A6. The second two, which are vectors, are not atomic and neither A0 nor A1 is relative to A6. The vectors are therefore to be preferred in normal use.
User heap areas are allocated as multiples of 8 bytes and an 8 byte header is prefixed to each allocation. The areas are set up as a linked list within the main user heap, and the user heap allocation traps scan the heap looking for bits big enough to fulfil the request. The header is in the following format :
|$00||Length of this block|
|$04||Pointer to the next free space in this user heap|
There is an example of the use of heaps here.