|
|
Bad Habit #2: misuses of dynamically allocated memoryBy 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:
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:
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:
Disadvantages of heap allocated memory include:
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. |
|