Introduction
The use of C++ operators new and delete are a necessary evil for software developers as they provide the dynamic memory allocation for the creation and destruction of objects in much the same way that malloc and free functions do in C (and in fact, the C++ operators actually use their C counterparts for implementation and ultimately, they all boil down to the operating system HeapAlloc and HeapFree calls).
I refer to them as being evil simply because they are grossly inefficient and can lead to huge quantities of precious memory being wasted (not to mention the developers nightmare trying to unravel the mysteries of corrupt memory and dangling pointers!). Consider the issues you would have in trying to write a set of heap management routines where memory blocks of any size can be allocated and deallocated, and the order of allocation and deallocation is completely random.
Microsoft, I am sure have done a good job at trying to resolve all of the issues associated with heap management (I can't believe that I have just said that!), but their strategy of implementation is not public domain so we have no way of knowing.
This tutorial discusses what you as a software developer can do to help stop the evil heap monster from gobbling up memory, by discussing various 'coding malpractices' and what can be done to avoid them.
To Allocate Or Not To Allocate
It is all to easy to feed the heap monster through inappropriate use of the new. For example let us consider a function that creates an object class, calls a method of that class and then delete's that class before returning to the caller.
class message_sender {
private:
SOCKET s;
public:
void send_message(message_base * message, char * footnote) {
char * output_message = new char[4000];
char * payload = message->payload();
int length = message->length();
memmove(output_message, payload, length);
int footnote_length = strlen(footnote);
memmove(output_message+length, footnote, footnote_length);
send(s, output_message, length+footnote_length, 0);
delete [] output_message;
}
};
...
void foo::send(message_base * message) {
message_sender * sender = new message_sender(...); // Parameters not relevant to example
// Code to build footnote removed
sender->send_message(message, footnote);
delete sender;
}
Taken from actual code with the names changed to protect the The code above went 'live' at many customer sites (after thorough testing) and everybody was happy until a number of customers began complaining that their systems were becoming more and more sluggish, to the point where they were not usable. This is where yours truly got given the task of investigating the problem.
I quickly established that the client application's memory usage had increased to biblical proportions at those sites where this problem was occurring and the client application was being used considerably more than those sites that had not yet encountered the problem. The fix took a while to formulate, but the problem was resolved. The solution was simple (once the problem was understood). Here is the solution:
class message_sender {
private:
SOCKET s;
public:
void send_message(message_base * message, char * footnote) {
char output_message[4096];
char * payload = message->payload();
int length = message->length();
memmove(output_message, payload, length);
int footnote_length = strlen(footnote);
memmove(output_message+length, footnote, footnote_length);
send(s, output_message, length+footnote_length, 0);
}
};
...
void foo::send(message_base * message) {
message_sender sender(...); // Parameters not relevant to example
// Code to build footnote removed but included a few new statements that were retained
sender.send_message(message, footnote);
}
You will notice that I simply removed the dynamic allocation of both the object message_sender and the output_message as they were unnecessary and the root of the problem. Why? Well it's quite simple. Allocating blocks of memory of considerably different sizes which are temporary and followed with some retained memory allocations (the code removed from building the footnote) the repeated many times was causing the heap to grow at a steady rate. This is known as heap memory fragmentation.
So, the first golden rule for the use of the new and delete operators in C++ (or malloc and free in C) is do I really need to use them to allocate and deallocate this object or can I use the stack instead?
Size does matter
The heap is not very good for dealing with allocations of many different sizes. Microsoft claim to have addressed the problem of heap fragmentation to a degree (please read here for more information). In former incarnations of the heap, allocations were made on exactness i.e. if a free block of memory was 20 bytes in length, and 18 bytes were requested, then that free block was not considered as a viable allocation area. It would appear that recent releases of the operating system will now allocate the 18 bytes in a 20 byte free block (albeit wasting 2 bytes of space).
Whilst this may reduce the amount of heap fragmentation, memory wastage is increased. I suppose this is perhaps a trade-off - a sort of lesser of two evils scenario!
Very Small Memory Blocks
Every memory allocation incurs a 24 byte overhead, with a minimum of 32 bytes allocated. So, allocating a single character will take 32 bytes of memory. Can you see where this is leading. I sincerely hope so!!! Very tasty morsels for the hungry heap monster. Code as shown below is an absolute no-no:
char * array[1000];
for ( int x = 0; x < 1000; x++ ) array[x] = new char;
This would require 32000 bytes of heap space used to hold 1000 bytes of information! Put it another way, I'll lend you $1000 so you owe me $32000! Now, you may think that no developer would be so stupid as to use code like that, but I can assure you I have seen such code on many occasions written by different developers.
It is far better to allocate a character array of 1000 bytes which would take 1024 bytes of heap space.
Conclusion
Always question the use of the new and delete operators in C++ (or malloc and free in C). If you have to dynamically allocate space using these methods, try and use as few allocations as possible rather than many allocations.
Don't feed the heap monster!!!






MultiQuote





|