Page 1 of 1

Microsoft : Beware of the Heap Monster Rate Topic: -----

#1 Martyn.Rae  Icon User is offline

  • The programming dinosaur
  • member icon

Reputation: 540
  • View blog
  • Posts: 1,406
  • Joined: 22-August 09

Posted 05 April 2011 - 10:51 AM

*
POPULAR

Microsoft : Beware of the Heap Monster

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 idiot innocent

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!!!

Is This A Good Question/Topic? 20
  • +

Replies To: Microsoft : Beware of the Heap Monster

#2 David W  Icon User is offline

  • DIC supporter
  • member icon

Reputation: 275
  • View blog
  • Posts: 1,778
  • Joined: 20-September 08

Posted 10 April 2011 - 11:57 AM

Nice tutorial ... A good subject to elucidate. Thus ... when using containers in C++ like vectors, (or in C like my Cvec), and the programmer anticipates a large number of elements to process ... using v.reserve( That_large_num ); may be good coding practice ... before v.push_back( next_element ); gets called repeatedly.

This post has been edited by David W: 10 April 2011 - 06:13 PM

Was This Post Helpful? 0
  • +
  • -

#3 markkenzy  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 17
  • Joined: 11-November 10

Posted 18 April 2011 - 08:53 AM

Thank you, guy. I never imagine the use of new and delete operator is so evil like that. :D
Was This Post Helpful? 0
  • +
  • -

#4 PinoyAko  Icon User is offline

  • New D.I.C Head

Reputation: 2
  • View blog
  • Posts: 25
  • Joined: 01-June 11

Posted 01 June 2011 - 07:53 PM

Thank you for posting this nice article :)
Was This Post Helpful? 0
  • +
  • -

#5 sungchoiok  Icon User is offline

  • D.I.C Head
  • member icon

Reputation: 44
  • View blog
  • Posts: 140
  • Joined: 18-July 10

Posted 26 June 2011 - 02:55 PM

When I was developing a small utility, of which its executable file's size was around 40 Kbytes, I noticed that suddenly, I found my executable file size increased to around 80 Kbytes after a random build. I deleted all the code that I had written up to that point, line by line, and hit upon a realization that it was the NEW operator (dynamic memory allocation) that was causing the sudden increase.

That's when I met my heap monster...

This post has been edited by sungchoiok: 26 June 2011 - 02:56 PM

Was This Post Helpful? 0
  • +
  • -

#6 RetardedGenius  Icon User is offline

  • >>──(Knee)──►
  • member icon

Reputation: 126
  • View blog
  • Posts: 555
  • Joined: 30-October 10

Posted 23 November 2011 - 03:41 PM

Thank you for this excellent tutorial. I am writing a small C++ high resolution timer class at the moment and I was wondering if I really needed to dynamically allocate so many variables. This tutorial has helped to clear it up for me. :)
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1