9 years, 5 months ago.

Can't allocate more than ~3kb heap memory

Hello,

I'm running into a rather confusing issue. I have a program which appears to be running out of memory and crashing. So, I created a separate program to simply soak up as much memory as possible on the heap and figure out how much RAM is available. Here it is:

#include "mbed.h"


Serial pc(USBTX, USBRX);


int main()
{
    char* dummy;
    int index = 0;
    
    while(1)
    {
        dummy = new char;
        pc.printf("%u\r\n", index);
        index++;
    }
}

That gets to 3.4kb before crashing. Note that the mbed doesn't reset - it just sits there. The mbed in question is this: http://developer.mbed.org/platforms/mbed-LPC1768/ The datasheet quotes 32kb of RAM, yet it seems I can only access 3kb.

I created another program which allocates the memory on the stack instead:

#include "mbed.h"


Serial pc(USBTX, USBRX);


int main()
{
    char dummy[30000];
    int index = 0;
    
    while(1)
    {
        dummy[index] = 0x45;
        pc.printf("%u\r\n", index);
        index++;
    }
}

This successfully allocates the 30kb and then loops through the entire array, setting each element. This is basically as I expected.

It would appear that I'm able to allocate more memory on the stack than the heap...? After reading around, it seems that mbed should allow the heap/stack sizes to grow to whatever is required - is this no longer true? What's really going on here? Surely I must be doing something wrong!

Thanks for any help in advance - I'm tearing my hair out over this one.

2 Answers

9 years, 5 months ago.

The most likely problem is that the heap has an overhead to keep tracking of the allocated memory, and since you are allocating single bytes this will be very significant.

According the ARM there are two standard available heaps: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0067d/Cihfiabf.html. Assuming the default is used, then the minimum data size is 4 bytes, and an additional 4 bytes is used for the size field (since it needs to keep track of how much memory it should free if you free that block, and even then without an address of the next used block I would not know exactly how it tracks everything).

So that is 8 bytes per byte you allocate, which makes the 3.4kB into 27.2kB. Then I found out once that it uses a safety margin of something like 4kB between stack and heap (which imo has questionable usefullness). If you want you can disable it using:

#pragma import __use_two_region_memory

(Note: This completely disables heap/stack collision checks, it makes the compiler think that stack and heap reside in different memories). So then it is pretty much correct what you are seeing.

Finally it is known that for some reason the compiler currently doesn't nicely return a null pointer if new fails. When you use malloc it will do so.

Accepted Answer

Thanks very much for the detailed explanation - looks like you hit the nail on the head when you take all that into account. I had no idea the overheads were so large - I'm quite surprised!

posted by Oliver Foyle 16 Nov 2014
9 years, 5 months ago.

The issue is related to the fact that each heap allocation has to also use extra memory for each allocation to track things such as state (free, in-use) and size. Most allocators also align their heap allocations to 8 bytes. This means that each of your allocations is probably taking up 8 bytes instead of the single byte that you expect. In your case that would mean that you have actually used 26 kbytes of the heap for your 3400+ allocations.

I changed your code to allocate 1 kbyte per allocation and got up to 25600 bytes before failing.

#include "mbed.h"
 
 
Serial pc(USBTX, USBRX);
 
 
int main()
{
    char* dummy;
    int index = 0;
    
    while(1)
    {
        dummy = new char[1024];
        pc.printf("%u\r\n", index * 1024);
        index++;
    }
}

I hope that helps,

Adam

Thanks a lot for the example. Goes to show the overhead isn't quite what I expected!

posted by Oliver Foyle 16 Nov 2014