Solutions for Developers, Services for Companies, Software for Users
 

Home
Up



bottrap link

A blog is now available Here.

Interested in becoming an Affiliate and earning 25% of all generated sales?  Click Here.

Handmade Art and
Treasures!

Handmade Art and Treasures: Fannys Follies

25% off for first-time customers at VistaPrint!

 

  

Bad Habit #2: misuses of dynamically allocated memory

By James R. Twine (Copyright 2002, James R. Twine)

[Previous Article In Segment] [Next Article In Segment]

Dynamically (or heap) allocated memory is a wonderful thing.  It gives us the ability to process data of varying sizes by allocating only what is necessary for that data.  A wonderful feature, to be sure.  However, it is quite easy to abuse.  More importantly, it is quite easy to abuse indirectly (this is covered in more detail in the Next Article).

First, a brief discussion on stack allocated vs. dynamically allocated memory.  Memory allocated from the stack has two major advantages over heap allocated memory:

  1. It is comparatively FAST.  In fact, it is likely the fastest way to allocate a block of memory in a program, because all that is really required is the repositioning of the stack pointer (machine architecture differences notwithstanding).
     
  2. It does not require any additional management.  When your code leaves scope, the stack is reclaimed.  Poof, like magic.  This is an especially good point when using languages that employ exception handling and stack unwinding; stack-allocated objects get cleaned up automatically.

Those two reasons alone are good enough to use the stack whenever possible.  There are, however, some things to be aware of when using the stack:

  1. Stack space is highly limited when compared to heap space.  Win32 applications, for example, normally have a stack of 1MB (although this can be adjusted).  Sounds like a lot, but most of the time you do not know where in the call stack your code is being called from, nor what the functions before you have allocated from the stack.  You may have 900KB of stack available, or you may only have 9KB.
     
  2. Stack allocated memory cannot persist correctly beyond its scope of allocation.

All in all, the stack allocated memory is well suited for memory/buffers that have a short lifetime, and known small-size requirements.

Heap allocated memory is not limited to the available stack, but usually to the amount of resources available on the target system.  It is allocated from a free pool of memory (the heap), and returned to the heap (deallocated) when it is no longer required.  Advantages of heap allocated memory over stack allocated memory include:

  1. There is often a large amount of heap space available.  Usually many times more than the available stack size.
     
  2. Heap allocated memory is never reclaimed automatically: the developer has to ensure that steps are taken to return this memory when it is no longer required (otherwise it "leaks", and is often not made available again).  This means that memory can be allocated at one location in scope, used in a completely different scope, and deallocated in yet another scope.

Disadvantages of heap allocated memory include:

  1. Heap allocated memory is never reclaimed automatically!  Resource leaks are some of the most common software bugs around.  These can and will cripple a system, rendering it completely unusable in severe cases (and has happened in the past!).
     
  2. Allocating (and deallocating) memory from the heap required additional overhead from the runtime libraries, and possibly even the underlying operating system.  Translation: heap operations are much slower than stack operations.

Now, with all that being said, the heap sounds like a pretty good thing, right?  Problem is, some developers view it as too good (i.e. free), and as a result, it gets abused.  Developers often allocate and deallocate heap memory recklessly and without care for the steps required to keep the heap in good working order.  Careless allocation and deallocation of heap memory can also result in a fragmented heap.  When the heap is fragmented, extra work may be required to satisfy a large allocation request.  Just like how a disk defragmenter works, the heap may have to move blocks around in order to get a large enough free block.  This, too, takes time.

Best way to avoid abusing the heap: do not dynamically allocate memory unless you really need to.  For example, if the memory you are allocating does not need to live beyond the scope of its allocation (and it is small enough), it can come from the stack.

Examples of this simple idea being ignored can be found everywhere.  In the MSDN® documentation, there are many examples.  One such example discusses how to rotate text using GDI functions.  This example is especially bad, because this is the kind of code that inexperienced developers turn to in order to learn something.  This is clearly the wrong thing to be learning.  Some of the lines from the example are shown here:

{
    PLOGFONT plf = (PLOGFONT)LocalAlloc( LPTR, sizeof( LOGFONT ) ); 
    HFONT    hFnt = NULL;

    strcpy( plf -> lfFaceName, "Arial" ); 
    plf -> lfWeight = FW_NORMAL; 
    hFnt = CreateFontIndirect( plf ); 
    //
    // [...Other Code Deleted...]
    // 
    LocalFree((LOCALHANDLE) plf); 
}

This is a great example of how NOT to write code.  The LOGFONT structure is relatively small, and does not need to live beyond its scope of creation.  In other words, there is absolutely no reason for its memory to have been dynamically allocated.  Additionally, code like this is often found in screen paint/updating routines, which are called a LOT

This is a prime example of where the stack should have been used.  This example is easily rewritten:

{
    LOGFONT	lfFont; 
    HFONT    hFnt = NULL;

    strcpy( lfFont.lfFaceName, "Arial" ); 
    lfFont.lfWeight = FW_NORMAL; 
    hFnt = CreateFontIndirect( &lfFont ); 
    //
    // [...Other Code Deleted...]
    // 
}

Now, we do not waste time with unnecessary direct heap operations, we do not have to take the extra step of freeing any heap allocated memory, and we may also are in a better position to take advantage of locality: all of the variables used by the function are now "near" each other (possible cache benefit).  Also, when dealing with languages like C++, proper exception handling practices dictate that extra work would have been required to ensure that the allocated memory is freed if/when any exceptions occur.  By moving to stack allocated memory, this extra code is obviated.

Moral of this story: never resort to dynamically allocated memory unless you are positive that you need to.  And even then make sure again before deciding to do it.  The decisions you make now can have lasting repercussions as your software moves through its development lifecycle.
 

 [Previous Article In Segment] [Next Article In Segment]

The contents of this web site are Copyright ©1998-2006 by JRTwine Software, LLC. All Rights Reserved. 
Legal and Copyright Information.
bottrap link