Memory Management: 2.2 The Structure of Memory: Main Memory

Up: GEOS SDK TechDocs | Up | Prev: 2.1 Expanded/Extended Memory | Next: 3 Using Global Memory

Memory available to applications is organized in a data structure called the Global Heap. The size of the global heap may vary from machine to machine and can change from session to session, but during a single execution of GEOS, the heap size remains constant. Usually, the global heap occupies approximately 450K bytes of a 640K system.

When an application requests memory, it is allocated a "block" on the heap. Blocks may be of almost any size but may not be larger than 64K. (However, the heap is most efficient when blocks are 2K-6K in size; see Memory Etiquette .) Every block is allocated a unique handle (described below) from the Handle Table; by calling a memory manager routine, the application can translate a handle to a conventional pointer.

When GEOS shuts down, all the blocks on the global heap are freed, even if they are locked or fixed. If an application will need to store the data over a shutdown, it should make the block part of a VM file, which it can then reopen when it restores from state. The GEOS kernel attaches object blocks to system VM files and takes care of storing them to state and restoring them when GEOS restarts.

Blocks and Handles

GEOS segments memory into blocks. A block is simply a number of contiguous bytes on the global heap in which code or data may be stored. Any given block may be of any size but must be less than 64K, due to the segmented addressing scheme used by the 80x86 processors. A block's size is determined when it is allocated--the process that requests the memory must specify the desired size.

To facilitate efficient memory usage and to present all applications with enough memory to function, blocks are dynamic in nature. This means that unless a block is fixed or has been locked (see Accessing Data in a Block ), there is no telling its precise address on the heap, or indeed whether it is on the heap at all. For this reason, applications are not given the address of an allocated block; rather, they are given the block's handle.

Memory handles are indexes into the global handle table. Geodes may not access the table directly. When they want to access the memory indicated by a handle, they pass the handle to the memory manager and are returned the segment address of the block's current location on the heap.

In addition to storing the actual address of the block, the handle table entry records the block's attributes, such as whether the block is discardable, swapable, or fixed. The memory manager uses all these attributes to manipulate the block.

Enabling Block Access

Dynamic memory, while providing significant benefits, poses one major problem: What happens if a block is accessed while being moved, swapped, or discarded? GEOS responds to this problem with the implementation of a system for locking and unlocking blocks. When a block is locked on the heap, the Memory Manager may not move, swap, or discard it until it is unlocked. This locking mechanism allows applications to gain control over their memory during periods of access and relinquish it when it is not in active use. Applications, however, should not leave blocks locked for extended periods as this may interfere with heap compaction.

When a process wants to use a block, it instructs the memory manager to lock the block by calling MemLock() (see MemLock() locks a block on the heap. It is passed the handle of the block; it returns a pointer to the start of the block on the heap. If the block has been discarded, MemLock() returns a null pointer. ). The application passes the handle of the block, and the memory manager locks the block and returns a pointer to the block's area in the global heap. While a block is unlocked, the memory manager can, depending on the block's category, move the block on the heap, swap it to disk or extended/expanded memory, or discard it altogether.

Types of Blocks

When a geode requests memory, it may specify how that memory is to be treated by the memory manager. The memory request includes a set of HeapFlags (see HF_FIXED The block will not move from its place in the global heap until it is freed. If this flag is off, the memory manager may move the block when it is unlocked. If it is on, the block may not be locked. This flag cannot be changed after the ) which specifies how and when the block can be moved. Broadly speaking, memory blocks can be divided into four categories:

Fixed blocks must be declared as such when they are allocated, and they remain so until they are freed. However, non-fixed blocks may become or cease to be discardable or swapable after they are created. To enable or disable these characteristics, call the routine MemModifyFlags() (see MemModifyFlags() is used to change a block's HeapFlags record. It takes three arguments: The handle of the block, the HeapFlags to turn on, and the HeapFlags to clear. It returns nothing. Not all HeapFlags can be changed after a block is created ).

Maximizing Free Space in Memory

Moveable, swapable, and discardable blocks are allocated from the top of the heap using a first-fit method. Fixed blocks are allocated from the bottom of the heap. If there is not enough contiguous free memory to satisfy an allocation request, the memory manager attempts to shuffle moveable blocks in order to place all free memory together in one large mass.

This shuffling is called heap compaction. If the free space resulting from compaction still is not enough, blocks are discarded or swapped to liberate more free space, and the heap is again compacted. Because of the multitasking nature of GEOS, compaction occurs in the background and is invisible to both the user and applications. The memory manager will also periodically compact memory during periods of low activity; this helps insure that there will be memory instantly available for a sudden large demand (e.g. when an application is launched).

The compaction is not arbitrary. The kernel decides which blocks to swap or discard based on recent usage patterns. This means, for example, that if you haven't used routines in a code resource for a while, that resource is more likely to be discarded than the resources you've accessed recently. (For this reason, geodes normally isolate their initialization code in one resource, which can be discarded later.)

A block left locked for extended periods could interfere with heap compaction. Suppose, for example, that the moveable locked block in the middle of the heap were left locked during an application's entire execution. Essentially, this would cause the heap to be fractured into two subheaps, making compaction more difficult and possibly slowing the system down.

All compaction, swapping, and discarding are functions of the Memory Manager. Applications need only indicate how much space is needed and when space can be freed. Applications may also resize blocks at will; if necessary, the memory manager will compact the heap to accommodate the request.

Block Attributes

HeapAllocFlags, HeapFlags

Blocks are allocated with certain flags that help the Memory Manager manipulate memory efficiently. These flags can be found in the GEOS file heap.h , which should be included in all applications that plan to allocate memory dynamically with the memory manager routines.

The flags fall into two categories: those used when the block is allocated (stored in a record called HeapAllocFlags ) and those used to describe the block as it is manipulated (stored in a record called HeapFlags ).

The HeapAllocFlags record is used to determine what qualities the memory manager should give the block when it is first allocated. Some of these flags are also relevant when memory is being reallocated. These qualities include:

HAF_ZERO_INIT
Upon allocation, initialize data in block to zeros.
HAF_LOCK
Upon allocation, the block should be locked on the global heap. Use MemDeref() ( MemDeref() is passed the handle of a block on the global heap; it returns the block's address on the global heap. As noted above, this routine is useful when you allocate a fixed or locked block. If the block has been discarded, it returns a nul ) to get a pointer to the block.
HAF_NO_ERR
Do not return error codes; system error if block cannot be allocated. Use of this flag is strongly discouraged.
HAF_OBJECT_RESOURCE
This block is an object-block. This is set by the system only .
HAF_UI
If both HAF_OBJECT_RESOURCE and HAF_UI are set, the memory manager will set the block to allow the application's UI thread to manipulate objects in the block. This is set by the system only .
HAF_READ_ONLY
This block's data will not be modified.
HAF_CODE
This block contains executable code.
HAF_CONFORMING
If this block contains code, the code may be run by a less privileged entity. If the block contains data, the data may be accessed or altered by a less privileged entity.

Once a block is allocated, it has certain properties that govern how the Memory Manager manipulates it. These properties are determined by the HeapFlags . The HeapFlags also contain data about whether the block has been swapped or discarded. These flags are stored in the block's handle-table entry, so they can be retrieved without locking the block. To retrieve the flags, call the routine MemGetInfo() with the flag MGIT_FLAGS_AND_LOCK_COUNT. (See MemGetInfo() on MemGetInfo() is a general-purpose block information routine. It is passed two arguments: the handle of the block, and a member of the MemGetInfoType enumerated type. The return value is always word-length; however, its significance depends on th .) Some of the flags can be changed after the block has been allocated; for details, see MemModifyFlags() on MemModifyFlags() is used to change a block's HeapFlags record. It takes three arguments: The handle of the block, the HeapFlags to turn on, and the HeapFlags to clear. It returns nothing. Not all HeapFlags can be changed after a block is created . The flags include

HF_FIXED
The block will not move from its place in the global heap until it is freed. If this flag is off , the memory manager may move the block when it is unlocked. If it is on , the block may not be locked. This flag cannot be changed after the block has been allocated.
HF_SHARABLE
The block may be locked by geodes other than the owner. This flag can be changed with MemModifyFlags() .
HF_DISCARDABLE
If the block is unlocked and space is needed, the memory manager may discard it. This flag can be changed with MemModifyFlags() .
HF_SWAPABLE
If the block is unlocked and space is needed, it may be swapped to expanded or extended memory or to the hard disk. This flag can be changed with MemModifyFlags() .
HF_LMEM
The block is a local-memory block, managed by the LMem module (see the Local Memory chapter). The flag is set automatically by LMemInitHeap() . It can be changed with MemModifyFlags() ; however, an application should not change this flag.
HF_DISCARDED
The block has been discarded by the memory manager. Only the system can set or clear this flag.
HF_SWAPPED
The block has been swapped to extended or expanded memory or to the hard disk. Only the system can set or clear this flag.

Up: GEOS SDK TechDocs | Up | Prev: 2.1 Expanded/Extended Memory | Next: 3 Using Global Memory