Appendix C - Memory Management Issues



The Macintosh memory model is based on the concept of the heap: an area of memory where items are placed as they are needed. Items are placed on the heap in the first available space when they are created, and the spaces which were occupied by deleted items are returned to the heap and made available for new items. Pointers or Handles (pointers to pointers) are directed at the items when they are placed on the heap. The Memory Manager part of the Operating System deals with all program and system requests for creation and deletion of handles, pointers and blocks. Memory blocks on the Macintosh come in two types: relocatable and non-relocatable ones.


fig. C.1 - A pointer to a non-relocatable block.


A pointer to a non-relocatable block never changes since the block itself cannot move (see figure C.1).

fig. C.2 - A handle to a relocatable block.


A pointer to a relocatable block can change since the block can move (see figure C.2). For this reason the memory manager will maintain a single non-relocatable master pointer to every relocatable block. This master pointer is created when the block itself is created, and is made to point to it. If the block needs to be moved, the value in the master pointer is modified appropriately by the memory manager. The use of handles allows the memory manager to consolidate free areas by compacting the heap. This involves moving relocatable blocks together to avoid the fragmentation of the heap with spaces which are too small to be of any further use, or by moving items pointed to by handles into appropriate places. Having non-relocatable blocks causes "islands" to be created in the heap, which can help to cause fragmentation. For this reason handles are used as much as possible, in preference to pointers, and any permanent non-relocatable blocks are created early in a program's execution, since this will mean that they are low down in the heap and hence less likely to get in the way of relocatable blocks being created and destroyed. One potential problem with relocatable blocks comes when such a block is being accessed when the memory manager starts compacting the heap, and thus moves the block. We will look at a situation where this may occur. Pascal defines a statement "with", which allows access to structures without recourse to continually specifying the full "path" to the structure. Figure C.3 shows a sample program which will encounter an error at run-time, or at least produce spurious results.
 program testprog;
 type
   myDataStructure = record
     I1:integer;
     I2:integer;
     AStr:Str255;{a string of 255 characters}
     TheData:array [1..40] of integer;
   end;
   myDataPtr:^myDataStructure;{a pointer to such a structure}
   myDataHndl: ^myDataPtr;{a handle to such a structure}
 var
   mydata:myDataStructure;
   myHndl:myDataHndl;
   myptr:pointer;
 begin
  {create a handle to a relocatable block large enough to hold mydata}
   myHndl:=myDataStructure(NewHandle(SizeOf(MyDataStructure)));
   with myHndl^^ do {de-reference the handle to get the record components}
   begin   
     I1:=10;
     myPtr:=NewPtr(20000);{a large non-relocatable block gets created here}
     {and now it's possible that the handle to myDataStructure has moved}
     I1:=I1+50;{and this line may well corrupt memory, since although}
     {the handle is pointing somewhere else, the with statement }
     {de-referenced the handle in advance - before it was moved by the}
     {memory manager.}  
   end;{of the "with" stuff}
 end.
 
fig. C.3 Pascal program in which a moving handle causes an error.


In this case, the relocatable block moved while it was being accessed, and the method of access (using with), meant that the address of the block was calculated in advance of the block moving, rendering those addresses invalid when the block finally moved. In order to avoid this problem it is possible to get the Memory Manager to "lock" a relocatable block to prevent it from moving, and a programmer must be wary of accessing unlocked relocatable blocks. In order to get the least possible memory fragmentation, relocatable blocks should only be locked when necessary, and then unlocked when they are no longer currently in use.

back to Appendix B. forward to Appendix D