Heap sizes
What
I was wondering about the size of the heap that's allocated to a program. Well, actually somebody asked me if there was a way to get the size of a memory region in C when you only had the pointer to it (the answer is, unfortunately, no). So I looked into the correlation between the size of allocated memory in the program and the heap size.
How
I used the following C program to determine the heap size.
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
long length = 0;
long bytes = 0;
void catch_sigsegv(int i) {
signal(SIGSEGV, catch_sigsegv);
fprintf(stdout, "%li %li\n", bytes, length);
exit(EXIT_SUCCESS);
}
int main(int argc, char **argv) {
extern int errno;
bytes = atol(argv[1]);
char *mem = (char *) malloc(bytes * sizeof(char));
if(mem == NULL) {
fprintf(stderr, "%s\n", strerror(errno));
return EXIT_FAILURE;
}
signal(SIGSEGV, catch_sigsegv);
while(1) {
*(mem++) = 1;
length++;
}
return EXIT_SUCCESS;
}
It allocates the amount of memory passed to the program as first argument (in bytes) and then traverses the allocated region and attempts to write to it. Sooner or later, a segmentation fault occurs and the handler prints the number of bytes allocated and the heap size.
All tests were done on a machine running a 2.6.14 kernel, glibc 2.3.6 and gcc 3.4.6 (everything in the gentoo flavour). I've also tested with the Intel C Compiler, version 7.1, but apart from more notices during compile the behaviour was the same.
The heap size when using malloc() was always at least 135160 bytes. It also increased before the allocated memory was larger than the heap. So, to compare it to something at a lower level, I rewrote the memory allocation in line 22 to use mmap().
char *mem = (char *) mmap(NULL, bytes * sizeof(char), PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, getpagesize());
The minimum heap size is getpagesize() (which happens to be 4096 bytes on my system) here, and it only increases when the amount of memory allocated is larger than the heap.
I've plotted bytes allocated against heap size for both malloc() and mmap() below.
As you can see, it's a nice step function. The heap size increases by the page size for every page size increase in allocated memory.
There was some weirdness to it though. The end of the plot was not chosen randomly, when I increased the size of allocated memory beyond this point, the segmentation fault wouldn't be handled by my custom handler anymore. It seems that from that point the loop that wrote to the heap overwrote the pointer to the signal handler. When I changed the writing line to
I got a compiler warning, but SIGSEGV was handled as intended again. But the heap size suddenly increased by more than the page size, indicating that it really somehow went beyond the heap memory into the stack. Another sign of this was that it took a larger number of bytes to allocate for malloc() than mmap() to cause the uncaught SIGSEGV (take a look at the diagrams at
http://lwn.net/Articles/91829/? to see the memory layout). So, we have ourselves a classic "buffer overflow" effect.
I wasn't able to reproduce this effect on another machine, so I guess my particular choice of flags when compiling the glibc (most notably -fstack-protector) caused this behaviour. I don't know enough about the details though to explain why.
Conclusion
I was a bit surprised to see that the default heap size with malloc() is so large. It all becomes clear when taking a look at the memory layout though -- mmap() starts allocating memory higher up, so the additional memory is there, this method just doesn't see it. The dynamic memory region will always be at least 135160 bytes.
Quite an entertaining and insightful afternoon.
There are no comments on this page. [Add comment]