16.2.3 Managing Global Memory Objects

The global heap is the Windows system-wide memory resource that is shared among applications. An application may request Windows to allocate memory objects out of the global heap by calling GlobalAlloc, the same function that Windows itself calls to allocate internally used memory objects. By using the global memory functions described in this section, you can take advantage of the same memory-management mechanisms Windows uses for its own purposes. In addition, by using these functions, your application can compete or cooperate with the system itself with essentially the same privileges. Misusing these privileges reduces your application's ability to cooperate with Windows and other applications.

The following considerations may help you determine whether to allocate memory for a given data object out of the global heap or the local heap:

You should address a memory object allocated from the local heap by using a near pointer (after you dereference the handle by using LocalLock). On the other hand, you should address a memory object allocated from the global heap by using a far pointer (after you dereference the handle by using the GlobalLock function).

An application's local heap is a relatively scarce memory resource, since it must fit in the application's automatic data segment (limited to 64K bytes) along with the stack and static data; the global heap is much larger.

If a memory object is in the current working set of your application, you should attempt to design it as a local object to take advantage of the more efficient near addressing. The current working set is data that you must frequently access during a fairly lengthy operation. Objects that are less frequently accessed belong in the global heap. For some applications, it might make sense to transfer data between the application's local heap and the global heap as the working set changes.

When designing the structure of global memory objects, you often have the choice of breaking them down into elementary objects or consolidating them into larger objects. In making this choice, you should consider the following:

Each global memory object carries an overhead of at least 20 bytes.

Global memory objects are aligned on 32-byte boundaries. The first 16 bytes are reserved for certain overhead information. In both standard-mode and 386 enhanced-mode memory configurations, there is a systemwide limit of 8192 global memory handles, only some of which are available to any given application.

In general, you should avoid allocating small global memory objects. A small object (128 bytes or less) carries at least a 15 percent space overhead, plus the memory that is wasted if the object's size (plus 16 bytes) is not a multiple of 32 bytes. This overhead may be justifiable in some cases, but you should weigh carefully the overhead involved. You should especially avoid allocating a large number (many hundreds) of small global objects if they can be consolidated into fewer, larger global objects. This consolidation not only eliminates space overhead but also avoids unnecessary use of the limited number of global memory handles.

With these considerations in mind, how you manage objects in the global heap is similar to how you manage memory objects in a local heap. For information about managing local memory, see Section 16.2.2, “Managing Local Dynamic-Data Objects.”

16.2.3.1 Allocating Memory in the Global Heap

You call the GlobalAlloc function to allocate an object of specified size in the global heap. Windows manages memory objects in the global heap according to the same classifications used for memory objects in a local heap: fixed, movable, and discardable.

The same mechanisms for compacting memory that are applied in managing a local heap also apply to the global heap. Thus, you may specify GMEM_NODISCARD or GMEM_NOCOMPACT when you call the GlobalAlloc function. For details, see the discussion of LMEM_NODISCARD and LMEM_NOCOMPACT under the description of the LocalAlloc function in Section 16.2.2.1, “Allocating Memory in the Local Heap.”

GlobalAlloc returns a handle to the allocated global memory object. If memory in the global heap is not available, GlobalAlloc returns NULL. It is always important to check the return value from GlobalAlloc, since you have no guarantee that your allocation requests can be satisfied. Most of the functions that manage global memory require this handle to identify the memory object.

16.2.3.2 Locking and Unlocking a Global Memory Object

You can dereference the handle to a global memory object by calling the GlobalLock function. GlobalLock returns a far pointer that is guaranteed to remain valid until you subsequently call the GlobalUnlock function.

GlobalLock must lock the object by fixing it in memory to ensure that the pointer it returns will remain valid until you call GlobalUnlock. Because it has locked the object, GlobalLock increments a lock count for the object. This count helps prevent the object from being discarded or freed while it is still being used.

Windows need not fix the object in memory unless it is discardable. The pointer will always be valid whenever the object moves in linear memory. Because Windows does not lock the object in memory, GlobalLock does not increment the lock count for a nondiscardable object. GlobalUnlock decrements the lock count of an object only if GlobalLock incremented it for the object. However, you must still call GlobalUnlock when you no longer need the pointer returned by GlobalLock.

In addition to GlobalLock and GlobalUnlock, several other functions affect the lock count for an object:

Increments lock count Decrements lock count

GlobalFix GlobalUnfix
GlobalWire GlobalUnWire
LockSegment UnlockSegment

For more information about how these functions affect a global memory object and its lock count, see the Microsoft Windows Programmer's Reference, Volume 2. The GlobalFlags function returns the lock count of a global memory object as set by these functions.

As noted earlier, it is not necessary to call LocalLock to dereference a local handle if the object is allocated as LMEM_FIXED. There is no similar capability for fixed global objects. Even fixed global objects must always be locked to dereference the handle.

The following example uses GlobalLock to dereference the handle of a movable global object:

HGLOBAL hGlobalObject;
char FAR* lpGlobalObject;

if (hGlobalObject = GlobalAlloc(GMEM_MOVEABLE, 1024)) {
    if (lpGlobalObject = GlobalLock(hGlobalObject)) {

        /*
         * Use lpGlobalOBject as the far address of the
         * globally allocated object.
         */
          .
          .
          .

        GlobalUnlock(hGlobalObject);
    }

    else {

        /* The lock failed. React accordingly. */

    }
}

else {

    /* The 1024 bytes cannot be allocated. React accordingly. */

}

If you allocate an object whose size is 64K or larger, you should cast and save the pointer returned by GlobalLock as a huge pointer. The following example allocates a 128K global memory object:

HGLOBAL hGlobalObject;
char huge * hpGlobalObject;

if (hGlobalObject = GlobalAlloc(GMEM_MOVEABLE, 0x20000L)) {
    if (hpGlobalObject
            = (char huge *) GlobalLock(hGlobalObject)) {

        /*
         * Use hpGlobalOBject as the far address of the
         * globally allocated object.
         */
          .
          .
          .

        GlobalUnlock(hGlobalObject);
    }

    else {

    /* The lock failed. React accordingly. */

    }
}

else {

    /* The 128K cannot be allocated. React accordingly. */

}

16.2.3.3 Changing a Global Memory Object

You can change the size or attributes of a global memory object while preserving its contents by calling GlobalReAlloc. If you specify a smaller size, Windows truncates the object. If you specify a larger size and also specify GMEM_ZEROINIT, Windows fills the new area of the object with zeros. By specifying GMEM_DISCARD or GMEM_NOCOMPACT, you ensure that Windows will not discard or move objects to satisfy the GlobalReAlloc request.

You can also call GlobalReAlloc to change the object's attribute from nondiscardable to discardable, or vice versa. Unlike LocalReAlloc, however, GlobalReAlloc can change a GMEM_FIXED object to GMEM_MOVEABLE or GMEM_DISCARDABLE. But it cannot change a movable or discardable object to a fixed object. To change the attribute of a global object, you must also specify the GMEM_MODIFY flag. For more information about doing this, see Section 16.2.2.3, “Changing a Local Memory Object.”

Be careful when you are changing the size of a global memory object if its size increases across a multiple of 64K. Windows may return a new global handle for the reallocated memory object. For example, this applies if you change the size of the object from 50K to 70K, or 120K to 130K. In standard mode, this applies if you change the size of the object across a multiple of 65,519 bytes (64K less 17 bytes).

Because of the selector-tiling technique Windows uses, Windows might have to search for a larger set of related selectors when the size of a global object increases across a multiple of 64K. If so, Windows returns the first selector of the larger set as the global handle. For more information about selector tiling, see Section 16.1.1.1, “Using Huge Memory Objects in Standard Mode.”

The following example reallocates a global memory object.

if (hTempHugeObject = GlobalReAlloc(hHugeObject,
        0x20000L,
        GMEM_MOVEABLE)) {
    hHugeObject = hTempObject;
}

else {

    /* The object could not be allocated. React accordingly. */

}

In this example, the temporary handle hTempHugeObject preserves the original handle in case GlobalReAlloc returns a NULL handle, indicating a failure to reallocate.

16.2.3.4 Freeing and Discarding Global Memory Objects

The GlobalFree and GlobalDiscard functions are identical to the LocalFree and LocalDiscard functions, except that they operate on global rather than local memory objects. For more information, see the discussion on LocalFree and LocalDiscard in Section 16.2.2.4, “Freeing and Discarding Local Memory Objects.”

16.2.3.5 Retrieving Information About a Global Memory Object

The GlobalSize and GlobalFlags functions provide current information about a global memory object. GlobalSize returns the current size of the object. GlobalFlags indicates whether the object is discardable and, if so, whether it has been discarded. It also indicates whether the object was allocated with the GMEM_DDESHARE or GMEM_NOT_BANKED flag.

16.2.3.6 Locking a Global Memory Object for Extended Periods

When you call GlobalLock to prevent a movable object from moving as other objects are manipulated in the global heap, you can hinder the ability of Windows to manage these other objects efficiently. To lock a discardable memory object for an extended period, use the GlobalWire function. To lock a nondiscardable memory object for an extended period, use GlobalLock. GlobalWire relocates the movable object to the lower area of the global heap reserved for fixed objects and then locks it. By moving the locked object to low memory, Windows can compact upper memory more efficiently but will require additional CPU cycles to move the object. Call GlobalUnWire to unlock the object. After the object is unlocked, it can migrate out of the fixed portion of the global heap.

16.2.3.7 Being Notified When a Global Memory Object Is to Be Discarded

If you want your application to be notified whenever Windows is about to discard a global memory object, call the GlobalNotify function. GlobalNotify is useful if you are writing a custom virtual-memory-management system that swaps data to and from disk, for example. You specify the address of the notification callback function in your application.

16.2.3.8 Changing When a Global Memory Object Is Discarded

As Windows manages the global heap, it employs a least-recently-used (LRU) algorithm for determining which global memory objects should be discarded when memory must be freed. You can call the GlobalLRUOldest function to move an object to the oldest position in the LRU list. This means that this object will be the most likely object to be discarded if Windows subsequently requires more memory. Conversely, by calling the GlobalLRUNewest function, you ensure that an object is least likely to be discarded.

These functions are useful, for example, for discarding initialization code when it is no longer needed. You could also use these functions if you were writing a custom virtual-memory-management system that swaps data to and from disk. With these functions, you can influence which objects are least or most likely to be discarded by Windows, thus minimizing the amount of disk swapping.

16.2.3.9 Freeing Global Memory in Low-Memory Conditions

Global memory is a shared resource; the performance of all applications depends on the ability of all applications to share that resource. When system memory is low, your application should be prepared to free global memory that it has allocated.

Windows sends the WM_COMPACTING message to all top-level windows when Windows detects that more than 15 percent of system time over a 30- to 60-second interval is being spent compacting memory. This indicates that system memory is low.

When your application receives this message, it should free as much memory as possible, taking into account the current level of activity of the application and the total number of applications running in Windows. The application can call the GetNumTasks function to determine how many applications are running.