Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Parallel

Memory Management With Macapp


SP 89: MEMORY MANAGEMENT WITH MACAPP

Curt is a software engineer at Apple Computer. He can be reached at Apple Computer Inc., 20525 Mariani Ave., MS 22-AE, Cupertino, CA 95014, or on Applelink at Bianchil.


Many Macintosh programmers equate MacApp with object-oriented programming and vice versa. Although it's certainly true that much of MacApp is object-oriented, it offers many services implemented with procedural programming as well. Among those services are tools that help with the difficult problem of managing an application's memory space. These tools range from useful dubugging aids to techniques for detecting and recovery from memory allocation failures, culminating in a comprehensive strategy for implementing robust Macintosh applications in a dynamic memory environment. Because these tools are implemented as conventional Pascal units and supplied with source code, they can be used by any Macintosh program built with MPW, including those written in C.

The purpose of this article is to describe some of these tools so that MacApp and non-MacApp programmers alike can make use of them an perhaps even entice some non-MacAppers into giving MacApp a try. By the way, I assume you have a pretty good idea of how Macintosh Memory Manager works. If you don't, How to Write Macintosh Software by Scott Knaster has an excellent description, and it's funny, too! And there's always Inside Macintosh, though it lacks Scott's humor (unless you count the Munger). Also, this article is based on MacApp 2.0B9. Some details may not apply to previous versions of MacApp, including MacApp 2.0B5.

The Problem

One of the most difficult aspects of writing a Macintosh application is the pervasive use of dynamic memory allocation. The Macintosh user interface and the design of the Toolbox both encourage heavy use of dynamic memory allocation. For example, good Macintosh programs allow the user to open multiple documents and windows, limited only by available memory. Because there is no way to know in advance how many windows or documents can be opened on a given machine, dynamic allocation of window and document data structures provides the natural solution. Given that available memory is indeed limited, a major task in completing an application is ensuring that it properly handles the time when memory fills up and dynamic memory requests are no longer satisfied.

To complicate matters further, an application doesn't have complete control of its own heap! (See the accompanying box.) The Macintosh system dynamically allocates handles, such as code and system resources, in the application's heap. And the system isn't very polite about such matters: It doesn't inform the application when using its heap, nor does it recover gracefully if one of its requests fails. Furthermore, any number of foreign objects cant take up residence in your heap, the most common being the desk accessory, which uses your heap when MultiFinder isn't available. In other words, your explicit memory requests compete with the demands (they can hardly be called requests because they can't fail) from the Macintosh system for the very same memory space -- namely -- the application's heap.

MacApp's Strategy

Clearly, an overall strategy is needed for implementing a program in such an environment. The goals os the strategy should be twofold. First, it should implement a scheme that always maintains enough free space in the application heap to satisfy the system's needs. And second, the heap should always have enough space to carry out essential operations such as quitting, saving documents, or deleting data from documents. Though more subtle, the latter goal is just as important as the former. And it's easier said than done. Quitting an application, for example, typically requires loading code segments that are used only when terminating the program. If the program has allowed the user to use up too much memory for data, then those termination segments won't be loadable! (Don't laugh, I've seen this happen.)

MacApp's strategy is mostly implemented in a Pascal unit called "UMemory," which controls which memory requests succeed. The idea is to ensure that essential requests always succeed, whereas non-essential requests may fail. Examples of requests that must succeed are allocations for code segments (strictly speaking, MacApp's strategy is smart enough that only certain segments must be loadable), defprocs, and packages. An example of a request that may fail is the allocation of data structures to open a second (or third, and so on) document.

The way MacApp does this is by reserving enough space in the heap so that essential memory requests can be satisfied at any point in the program. Because the Macintosh Memory Manager doesn't know an essential request from a nonessential one, it's necessary to intervene in the process and give the Memory Manager some help.

Fortunately, the designers of the Memory Manager provided a mechanism that lets you do just that. It's called the grow zone function. The way it works is that whenever the Memory Manager finds that there isn't enough space in the heap to satisfy a memory request, it calls the grow zone function in the hopes of freeing additional space in the heap. The grow zone function is called repeatedly until either there is enough space in the heap to satisfy the request or the grow zone function is unable to free any more space, in which case the allocation request fails. By installing a grow zone function, a program can determine which requests succeed and which ones fail.

Permanent vs. Temporary Requests

MacApp's grow zone function distinguishes between essential and nonessential memory requests. In MacApp terminology, these are classified as temporary (essential) and permanent (non-essential). A useful way of thinking about this is that temporary requests are program-driven whereas permanent requests are user-driven. Memory allocated with a temporary request is freed when the program has finished with it; memory allocated with a permanent request is freed when the user has finished with it. Table 1 provides examples of each.

Given this distinction between temporary and permanent memory requests, how does MacApp take advantage of it? If you look at Table 1, you'll notice that the very nature of temporary requests is that they must succeed. For one thing, most temporary requests come from the system, which assumes success! Also, because these requests are program-driven, their success is usually essential to keep the application running. In other words, failure of a temporary request is usually fatal. On the other hand, the failure of a permanent request simply indicates a lack of memory to carry out a user operation such as opening another document. Assuming the application started with enough memory to perform a reasonable set of operations, failure of permanent requests isn't catastrophic. It simply indicates that the user needs to reduce memory usage, say by closing a document or deleting some data.


Table 1: Examples of temporary requests and permanent requests

Temporary Requests

Temporary requests usually come from the system, when it attempts to use the application's heap. Examples of such uses are:

  • Code segments, whose handles are allocated by the Macintosh operating system when a segment has to be loaded into memory.
  • Defprocs for the Window Manager, Control Manager, Menu Manager, and List Manager. They take the form of WDEF, CDEF, MDEF, and LDEF resources, respectively.
  • Packages, which take the form of PACK resources.
  • Fonts, which take the form of FONT and FOND resources. Handles for Toolbox data structures associated with windows, controls, etc.
  • Handles allocated by the Toolbox and operating system for temporary data such as printing data structures, regions for QuickDraw, the file list for Standard File dialogs, etc.
  • Handles allocated by the application for short-term use, such as handles that are created and disposed of within the same routine.
Permanent Requests

Most explicit requests by the application are permanent, and are used for data structures requested by the user, such as document data structures. These include:

  • Allocation of objects.
  • Most handles allocated by the application.

MacApp ensures the success of temporary requests by reserving enough space in the heap so that any temporary request succeeds (ultimately at the expense of permanent requests). The space is reserved simply by allocating a handle large enough to reserve it. This handle is known as the temporary reserve. Its size is determined by taking the total amount of space reserved for temporary requests and subtracting from it the sizes of the currently allocated temporary handles. Becuase its purpose is to reserve space, the size of the temporary reserve varies over time as the amount of temporary handles in memory increases or decreases.

Now, when a memory request is made and there is no room in the heap, the Memory Manager calls MacApp's grow zone function. It behaves differently depending on whether the request is temporary or permanent. The algorithm is shown in Table 2. The basic idea is never to let a permanent request use space reserved for temporary requests, thereby always having enough space to satisfy a temporary request. (The emerging reserve referred to in the algorithm will be described later in this article.)


Table 2: MacApp's grow zone function algorithm

For a permanent request:

    1. If the temporary reserve is too big, reduce its size to that actually needed.

    2. Otherwise, if there is a temporary handle that's not in use, purge it so long as the space held by the temporary reserve and the other temporary handles doesn't fall below the minimum that must be reserved.

    3. Otherwise, if the emergency reserve is intact, purge it as a last-ditch attempt to free space.

For a temporary request:

    1. If the temporary reserve exists, purge it. (It will be reallocated later.)

    2. Otherwise, if there is a temporary handle that's not in use, purge it. A temporary handle is considered to be in use only if it's locked.

    3. Otherwise, if the emergency reserve is intact, purge it as a last-ditch attempt to free space.


The algorithm raises some interesting questions. First, how is it that MacApp's grow zone function knows whether it's been called in response to a temporary request or a permanent one? And second, how does the grow zone function know if a handle is temporary and whether such a handle is currently in use or not?

The Permanent Flag

The answer to the first question is that MacApp's UMemory unit maintains an internal flag indicating whether it's in temporary or permanent mode. It is used by MacApp's grow zone function to decide what to do. The normal state of the flag is temporary, causing system requests (which tend to come at unpredictable times) to be temporary.

For making permanent requests, MacApp defines four memory allocation routines, each consisting of these steps:

    1. Set the flag to permanent

    2. Carry out the request

    3. Restore the state of the flag

These routines are shown in Table 3. The inclusion of SetPermHandleSize and SetPermPtrSize may seem odd at first, but increasing the size of a handle or pointer results in a memory request no different from creating the handle or pointer in the first place. An additional routine called PermAllocation is available for setting the flag to affect any number of subsequent requests. Thus, the two code fragments in Listing One, page 28, accomplish the same thing -- they allocate two handles as permanent requests.

Table 3: Permanent requestors

  Permanent Request                                             Inside Mac
  Routines                                                      Counterparts
  ______________________________________________________________________

  FUNCTION      NewPermPtr (logicalSize: Size): Ptr;            NewPtr
  FUNCTION      NewPermHandle (logicalSize: Size) Handle;       NewHandle
  PROCEDURE     SetPermHandleSize (h:Handle; newSize; Size);    SetHandleSize
  PROCEDURE     SetPermPtrSize (p: Ptr; newSize: Size);         SetPtrSize

Most allocations by the application itself are permanent. For this reason, creating objects with NEW uses NewPermHandle to allocate the object's handle. Likewise, most handles you create will be for long-lived data such as document or window data structures, so they should be allocated with NewPermHandle. Handles used for short-term use, such as those allocated and disposed of in the same routine, can be allocated with NewHandle.

Temporary Handles

The second question that needs answering is how MacApp's grow zone function finds temporary handles that are not in use. Actually, it's simple. MacApp maintains four lists of handles. The handles in these lists are considered temporary, and may be purged by MacApp's grow zone function if they aren't in use. The four lists are:

1. gCodeSegs, which contains handles to all the code segments

2. gSysMemList, which contains handles to all the non-ROM system and application LDEF, CDEF, MDEF, WDEF, and PACK resources

3. gApp1MemList and and gApp2MemList, which contain handles, usually to resources, that the application wishes to be subject to purging by MacApp's grow zone function

Note that handles to code segments and other resources exist whether or not the segment or resource is loaded into memory. (If it's not in memory, the handle's master pointer is NIL.)

The definition of "not in use" is simply that the handle is not locked. For example, when code in a segment is executed, the segment's handle is locked. When a segment is unloaded, it is simply unlocked and eligible for purging. Likewise, when a defproc is actually used, it is locked. When it's not locked, it's also eligible for purging. Normally all these unlocked handles would be purged by the Memory Manager long before it got to the grow zone function. But because MacApp wants to control when these handles are purged, it marks each of them as nonpurgable to prevent the Memory Manager from interfering. Thus, MacApp's grow zone function is able to choose when to purge them.

By the way, these lists are prioritized in terms of which handles get purged first. The order is gCodeSegs, gApp1MemList, gSysMemList, and gApp2MemList. That is, MacApp's grow zone function will purge unused code segments before purging unused system resources. Similarly, those handles in gApp1MemList are purged before those in gApp2MemList.

Determining How Much Space to Reserve for Temporary Handles

Essential to making this scheme work is ensuring that the right amount of space is reserved for temporary requests. If too little space is reserved, then the application could wind up being unable to satisfy a temporary request, which usually results in a system error. (Despite its frequency of appearance, the system error alert is not considered part of the Macintosh look and feel.) On the other hand, reserving too much space is wasteful, causing the application to require more memory than it really needs.

To determine how much space to reserve, MacApp uses the following formula:

    1. Add up the sizes of the code segments whose names are listed in all seg! resources seg! resources will be explained shortly -- their purpose is to list the segments that must be in memory at the time the application uses the greatest amount of code.

    2. Add to this total an arbitrary amount from all mem! resources. This lets you reserve space for noncode use, such as system resources; mem! resources are also explained later.

The result of this formula should be the maximum amount of space that the application will need to satisfy any temporary request. Note that I said should be. That's because both steps require that you provide information to MacApp -- namely, a list of code segments in step 1 and an additional padding amount in step 2. Thus, this scheme is only as good as the information you give it.

Accounting for Code Segments, or How to Use seg! Resources

Because allocating memory for a code segment is considered a temporary request, it is necessary to know how much space to reserve for code. A simple-minded approach would be to add up the sizes of all the code segments in the application, but that would be wasteful because it's doubtful a large program would ever need all of its segments in memory at one time. A better way is to add up the sizes of the code segments that must be in memory when the application uses the greatest amount of code. If enough space is reserved for that case, then clearly there will be enough space for code in all cases. To do this, you must determine which segments are required and the sizes of those segments -- which is exactly what seg! resources are for.

A seg! resource is simply a list of strings, each of which is the name of a segment. The segments listed in seg! resources are those segments that must be in memory at the point of greatest code usage. When the application is started, MacApp will add up the sizes of all the code segments listed in the seg! resources, arriving at the amount of memory that it reserves for code. The nice part is that you don't have to know the actual size of each segment, just the names of the segments involved.

MacApp defines one seg! resource of its own, shown in Listing Two, page 28. It lists MacApp's resident segments -- that is, the MacApp-defined segments that are always in memory --and conditionally lists MacApp's debugging segments. Normally you add one seg! resource of your own identifying which segments, in addition to those in MacApp's lists, are in memory at the point of greatest usage.

Obviously the key to making this work is knowing which segments to list in your seg! resources. MacApp's debugger makes this easy to do. First, you can request that the debugger interrupt the program any time the previous point of greatest code usage is exceeded. And second, when the point of greatest usage is identified, you can ask the debugger to list those code segments in use at the time. Voila -- you've got your seg! list.

Here's the step-by-step procedure:

    1. Build a debugging version of your application.

    2. Start the application by double-clicking its icon (or better, by double-clicking one of its documents). Immediately afterward, hold down the Shift, Option, and Command keys; this is how you break into the MacApp debugger. What you want to do is break into the debugger as soon as possible. When the program break occurs, the MacApp debugger window will appear, containing the cryptic message

stopped at Begin INITUMENUSETUP Seg#: 10

Command [BCDEFGHILMOPpiQRSbetaTWX?]:

The first line indicates where the program was interrupted, in this case at the start of the routine InitUMenuSetup, which is in segment number 10. The second line is the debugger's command prompt. Commands in the MacApp debugger are issued by typing a single character. At any debugger prompt, the characters accepted appear within brackets; typing a question mark displays a description of each command.

    3. Type x, then r. This causes MacApp to report via the debugger window each time the amount of memory used by code and system resources surpasses the previous maximum.

    4. Type x, then b. This causes MacApp to also interrupt the program each time the previous maximum is surpassed.

    5. Type g to resume execution of the program. It will be interrupted immediately with the debugger window showing a message similar to that in Example 1.

Example 1: Breaking into the MacApp debugger

  = = New maximum resources usage: 211884 = =
  stopped at Break CHECKRSRCUSAGE Seg#: 1
  Command [BCDEFGHILMNOP<img src="http://twimgs.com/ddj/ddj/images/ddj8913d/pi12.gif" alt="[pi]">QRS<img src="http://twimgs.com/ddj/ddj/images/ddj8913d/beta12.gif" alt="[beta]">TWX?]:

This indicates that a new all-time high has been achieved in the amount of memory required for code and system resources. Each time a new high is achieved, the program will be interrupted and this message displayed. Now, as soon as you type g to resume the application, this will probably happen again. In fact, this may happen several times until the application gets to the point where it's waiting for a user event.

More Details.

    6. Completely exercise the program, trying all its commands and options. What you're trying to do is find the one point in the application that requires the greatest amount of code and system resources. For many applications this occurs at program start-up (when the user double-clicked a document) when printing, or when quitting and saving a document. Of course, this will vary greatly depending on the nature of the application, and you should thoroughly exercise your program to make sure you've found the right place.

    7. Now that you know where the high point is, run the program again using the procedure just described and cause the known high point to be reached. When the program is interrupted at this point, type the following commands into the debugger: h, then s. This produces a list of the code segments in use at the time and looks similar to Example 2. (You can move and resize the debugger window in order to see the complete list). This list shows the set of segments that should be included in the seg! resources. A dot in front of a segment name indicates the segment is resident and is always in memory. An L in front of the name indicates the segment is locked in memory because it's currently in use. Note that other segments may be in memory too, but because they're not in use (and could be purged from memory if necessary) they're not shown. (A list of all segments, in memory or not, can be shown by typing h and Option-s.)

Example 2: An example of the segment list displayed by MacApp's debugger

  Command [JBCDEFGHILMOP<img src="http://twimgs.com/ddj/ddj/images/ddj8913d/pi12.gif" alt="[pi]">QRS<img src="http://twimgs.com/ddj/ddj/images/ddj8913d/beta12.gif" alt="[beta]">betaTWX?]: H

  Heap/Stack Cmd [+BDIMRS<img src="http://twimgs.com/ddj/ddj/images/ddj8913d/beta12.gif" alt="[beta]">?]: S
  Total # segments = 32
  * = resident, L = loaded
  $0008A22C Seg#:  1 *      Main                           24584 bytes
  $0008A184 Seg#:  4 *      GDebug                         10288 bytes
  $0008A18C Seg#:  6 *      GNonRes                        10004 bytes
  $0008A190 Seg#:  7 *      GFields                        16012 bytes
  $0008A19C Seg#: 10 L      GInit                           6984 bytes
  $0008A1A0 Seg#: 11 *      GInspector                     15600 bytes
  $0008A1A4 Seg#: 12 L      GOpen                          16640 bytes
  $0008A1AC Seg#: 14 L      GSelCommand                     9060 bytes
  $0008A1BC Seg#: 18 *      BBRes                            228 bytes
  $0008A1C0 Seg#: 19 *      GWriteLn                       16224 bytes
  $0008A1C4 Seg#: 20 *      GDebugger                      28932 bytes
  $0008A1C8 Seg#: 21 *      GPerformanceTools               5988 bytes
  $0008A1CC Seg#: 22 *      GMain                          11436 bytes
  $0008A1D8 Seg#: 25 *      GRes                           31700 bytes
  $0008A1DC Seg#: 26 *      ARes                            6016 bytes
  $0008A1E0 Seg#: 27 *      % MethTables                   10940 bytes

  Total loaded code = 220764
  Current temp space: locked = 220764, unlocked = 0, total = 220764
  Command [JBCDEFGHILMOP<img src="http://twimgs.com/ddj/ddj/images/ddj8913d/pi12.gif" alt="[pi]">QRS<img src="http://twimgs.com/ddj/ddj/images/ddj8913d/beta12.gif" alt="[beta]">TWX?]: O

You can either write these down or dump the list to a text file using the debugger's output redirection feature. This is done by typing o, then a file name such as Segment Listing. Type y to the prompt after entering the file name to cause debugger output to be displayed in the window while it is written to the text file. Then type h and s to list the segments again. This time the list is copied to the text file. Type o to close the output redirection file, and quit the application (by typing q in the debugger).

With the list of segments in hand, it's a simple matter to add your own seg! resource to the application. Make sure you assign it a resource ID of 256 or greater as anything less than 256 is reserved by MacApp. Also, list only those segments that do not appear in MacApp's seg! lists. Repeating a segment causes MacApp to reserve space for it each time it occurs in a seg! list.

Accounting for Noncode Usage (or How to Use mem! Resources)

As mentioned earlier, mem! resources can be used to increase the size of the temporary reserve. Actually, each mem! resource consists of three numbers. MacApp 2.0b9's mem! resources are shown in Listing Three, page 28. Like seg! resources, there can be as many mem! resources as needed.

The first value of each mem! resource is added to the size of the temporary reserve. This lets you account for noncode temporary requests, such as system resources or system-allocated temporary data structures used for things such as printing or perhaps complex region manipulation. MacApp includes three mem! resources of its own. The result is that 4K is always allocated for the temporary reserve, an additional 40K is added if the application prints, and another 2K is added if MacApp's debugger is included. Your mem! resource can reserve additional space if necessary to account for things like fonts, or it may not need to add anything to this total. (It may take some work to determine empirically how much your application needs. Of course, the more your reserve, the safer you are.)

MacApp sums the second values of each mem! resource to determine the size of a handle that reserves memory for emergency use. The emergency reserve was alluded to in the algorithm for MacApp's grow zone function. As its name implies, this handle is purged by the grow zone function as a last resort, when all other avenues have been exhausted. By definition, once this reserve is emptied, the application is in a low-memory situation, and MacApp will display an alert to that effect. In such a low-memory situation, your application should prevent the user from performing any operations that add to the application's memory requirements. MacApp's mem! resources reserve 4K for emergency use. You can add more if necessary.

The third values of the mem! resources are summed to determine the amount of space for the application's stack, in addition to the 8K automatically reserved by the Memory Manager. If you need a bigger stack, say because you're passing large data structures as parameters or defining them as local variables in procedures, you can do so by supplying the additional amount in a mem! resource. You can also decrease the stack space by using a negative number. Normally, it isn't necessary to change the size of the stack.

gApp1MemList and gApp2MemList

The lists gApp1MemList and gApp2MemList were described previously as containing handles considered to be temporary and subject to MacApp's control. The difference between the two is that handles in gApp1MemList are purged before those in gApp2MemList. Typically the handles put in these lists are for resources. For example, you may want to add the handles to font resources to gApp2MemList so that the fonts stay in memory for as long as possible.

Normally these lists aren't used. If you want to use of them, you must first create the list and then assign handles to it with MacApp's AddHandle routine:

      gApp1MemList := HHandle List(NewHandle(0));
   AddHandle(aResource, gApp1MemList);

Handling Low-Memory Conditions

A MacApp program is considered to be in a low-memory condition when its emergency reserve handle cannot be allocated. In this condition there will still be some free memory in the heap, giving the application a little breathing room. In fact, the larger the emergency reserve, the more memory is in a low-memory condition. Even in a low-memory condition, the application can continue to request memory and the Memory Manager will oblige until every last byte of the heap is allocated. After that, further requests will cause the program to bomb.

Consequently, whenever the program is in a low-memory condition, it is important that it not perform any operations that increase its memory usage. To that end, MacApp automatically displays an alert informing the user of the condition and disables the New and Open commands. You should take similar precautions of your own. To find out if the application is low on memory, call MacApp's MemSpaceIsLow function. If it returns true, then you should disable any operations that increase the memory burden of the program. Obviously, any commands that add data to a document, such as Paste, should be disabled.

On the other hand, commands that reduce the memory burden should be enabled, particularly the Clear command. The Cut command may not be effective in a low-memory condition because it sometimes increases the use of memory. This is because in addition to copying data from a document or view to the Clipboard, MacApp makes a copy of the existing Clipboard to make the Cut undoable.

Provided enough space has been reserved for temporary requests, a low-memory condition will only affect permanent requests (because MacApp releases its temporary reserve for temporary requests but not for permanent ones). Thus, MacApp's strategy is geared toward the ultimate failure of permanent requests as the user fills up memory, so it's important to test the result of every permanent request to verify its success. Two MacApp routines are useful for this purpose: FailNIL and FailMemError.

FailNIL has a single parameter, which is a handle, object, or pointer. If the parameter is NIL, then FailNIL signals failure. FailNIL can be used to test the result of NEW, NewHandle, NewPermHandle, NewPtr, and NewPermPtr, because each of these returns NIL if there isn't enough space to complete the allocation.

FailMemError can be used to test the result of any Memory Manager routine. It signals failure if the value of the Memory Manager's MemError function returns a nonzero value. MemError returns the result of the last Memory Manager routine that was called. This is useful for testing the result of SetHandleSize, SetPermHandleSize, SetPtrSize, and SetPermPtrSize.

Conclusion

Some people, when they see the amount of work that's gone into MacApp's memory management scheme, wonder whether it's really necessary. The fact is that if you want to build a truly robust Macintosh application, then a strategy of some sort is necessary to address the problems described in this article. By using MacApp, or its memory management scheme, your application has a proven strategy in place that's relatively easy to take advantage of.

Bibliography

Inside Macintosh, Volumes I- V. Apple Computer, Cupertino, Calif., 1985-1988. These are essential for writing any Macintosh program. Of particular interest to this article are the chapters on the Memory Manager, Resource Manager, and the Segment Loader.

Knaster, Scott. How to Write Macintosh Software. Rochelle Park, N.J.: Hayden Book Company, 1986. An extremely useful book. Chapters 2 and 3 have valuable information about life with the Macintosh Memory Manager.

MacApp, available from APDA at Apple Computer, Inc., 20525 Mariani Ave., MS 33-G, Cupertino, CA 95014-6299; 800-282-2732.

How the Macintosh System Uses an Application's Heap

The Macintosh system uses the application's heap for many purposes. It doesn't notify the application when this happens, and if there isn't enough space in the heap for the system's needs, the application will bomb. Here are the most common system uses of the application heap:

  1. The very code an application is made of gets loaded by the system into the application's heap on a segment-by-segment basis whenever a routine is called in a segment not currently in the heap.
  2. When the application is not run under MultiFinder, the system loads various system resources into the application's heap when the application uses them. These include:
    • Defprocs (short for definition procedures), which implement the basic low-level behavior of windows, controls, and menus. They take the form of WDEF, CDEF, and MDEF resources, respectively. There are also LDEF resources for the List Manager.
    • Packages, which are collections of code akin to libraries and get loaded into the application's heap whenever the application requires their services. In most cases the interface to the routines in a package provides no clue that those routines are in fact in a package. For example, a simply utility such as NumToString for converting integers to strings is contained in the Decimal-to-Binary package.
    • Fonts used by your application that are not in ROM. Fonts appear as FONT and FOND resources, and they can take up quite a bit of space.
  3. The system uses the application's heap for short periods to maintain various data structures. These uses include temporary regions for QuickDraw operations, the creation of file lists for Standard File dialogs, saving the screen bits behind a pull-down menu, and the allocation of data structures for printing.
  4. If the application is not run under MultiFinder, then desk accessories use the application's heap for their memory requirements. Even worse, ill-behaved desk accessories can leave handles in your heap and there's nothing you can do about it!

-- C.B.

<a name="l1">[LISTING ONE] 

Fragment 1:

aHandle := NewPermHandle(100);
anotherHandle := NewPermHandle(200);



VAR
    wasPermanent:	 BOOLEAN;
Fragment 2:


wasPermanent := PermAllocation(TRUE);	{ Subsequent requests are permanent }
aHandle := NewHandle(100);		{ Both NewHandle calls are considered }
anotherHandle := NewHandle(200);	{ permanent because perm. flag is set }
wasPermanent := PermAllocation(wasPermanent);   { Restore state of the flag }


<a name="l2">[LISTING TWO]

resource 'seg!' (kBaseMacApp,
#if qNames
    "BaseMacApp",
#endif
    purgeable) {
    {	"Main";
	"GMain";
	"ARes";
	"GRes";
	"BBRes";
	"%_MethTables";
#if qInspector || qDebug
	"GDebug";
	"GInspector";
	"GWriteLn";
	"GFields";
#endif
#if qDebug
	"GDebugger";
	"GNonRes";
#endif
#if qPerform
	"GPerformanceTools";
#endif
    };
};


<a name="l3">[LISTING THREE]

resource 'mem!' (kBaseMacApp,
#if qNames
    "BaseMacApp",
#endif
    purgeable) {
    4096,		// Add to temporary reserve
    4096,		// Add to permanent reserve
    8192		// Add to stack space (actually this first
			// one sets the base stack requirement to
			// be the same as for a Macintosh Plus).
};

resource 'mem!' (kPrinting,
#if qNames
    "kPrinting",
#endif
    purgeable) {
    43 * 1024,		// Add to temporary reserve
    0,			// Add to permanent reserve
    0			// Add to stack space
};

resource 'mem!' (kForDebugging,
#if qNames
    "ForDebugging",
#endif
    purgeable) {
    2048,		// Add to temporary reserve
    0,			// Add to permanent reserve
    0			// stack space
};


Copyright © 1989, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.