3

I'm a bit worried about the behavior of posix_memalign() and malloc() on my system. I have the following test code:

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

int
main()
{
  int i;
  float *data;
  for (i = 0; i < 5; i++) {
    assert(!posix_memalign((void**) &data, 16, 100000 * sizeof(float)));
    printf("data = %p\n", data);
  }
  free(data);
  for (i = 0; i < 5; i++) {
    assert(!posix_memalign((void**) &data, 16, 100000 * sizeof(float)));
    printf("data = %p\n", data);
  }
  return 0;
}

In the first 5 allocations, posix_memalign() returns high addresses. After I call free(), it suddenly switches to low addresses:

% ./aligned
data = 0x7f74f9974010
data = 0x7f74f9912010
data = 0x7f74f98b0010
data = 0x7f74f984e010
data = 0x7f74f93c4010
data = 0x929010
data = 0x98aaa0
data = 0x9ec530
data = 0xa4dfc0
data = 0xaafa50

The behavior is the same if I compile it as a C program (.c, gcc) and as a C++ program (.C, g++). The behavior is the same if I replace posix_memalign() by malloc().

Any idea what's going on? Thanks!

I'm using Linux 3.2.0 / x86_64 (Ubuntu) and gcc 4.6.3.

Ralf
  • 1,203
  • 1
  • 11
  • 20
  • 2
    Did you consider actually freeing ALL of your allocations, not just the last one? And there is absolutely no guarantee that allocations are in any particular order or any particular locality in relation to other allocations. – Mats Petersson Dec 13 '14 at 17:15
  • @MatsPetersson: In this case (posix_memalign(), then immediately free()) the switch to lower addresses also happens after the first free. – Ralf Dec 13 '14 at 17:21
  • Will this answer the question: http://stackoverflow.com/questions/1119134/how-do-malloc-and-free-work ? – facetus Dec 13 '14 at 17:33
  • 1
    @noxmetus: Thanks for the link, but no, I didn't find anything specific for my problem. [Edit:] To me it looks as if allocations are served from some reserved area at the upper end of the virtual address space for efficiency reasons (no call to brk() or sbrk()). This could actually be done without any heap management. As soon as free() is called, it has to use heap management and switches to different addresses. Actually I would have expected low addresses right from the beginning since the heap starts immediately above the code and static data range and grows upwards. – Ralf Dec 13 '14 at 17:40
  • Can you compile libc and step it in a debugger to see what is going on? – facetus Dec 13 '14 at 17:45
  • @noxmetus: Ugs, I had a look at that part of libc a couple of years ago hunting down a weird memory problem, and, frankly, I'd rather not look at that code again... ;-) – Ralf Dec 13 '14 at 17:52
  • But this is the only option to know for sure. You want the implementation details. Implementation details are there. – facetus Dec 13 '14 at 17:54
  • How is this a problem? – Yakk - Adam Nevraumont Dec 13 '14 at 17:59
  • 1
    @Yakk. The author didn't say it is a problem, he said he is worried. An inexplicable behavior makes any curious mind worried. – facetus Dec 13 '14 at 19:34
  • 1
    @Yakk: To explain why I asked this question: I had a similar problem a few years ago. Some library we used came with its own heap management system, basically a copy of an older version of libc (phantastic idea). It provided a free() which was linked to the code, but didn't have a posix_memalign() yet, so that was used from libc. So we actually had two heap manangement systems working on two different address ranges (a similar effect as seen here). It crashed, of course. I'm just worried something similar is happening here, though I don't link against any libraries, it should just be libc. – Ralf Dec 13 '14 at 23:05

1 Answers1

1

This behavior is perfectly valid and your code shouldn't depend on memory being allocated in any particular pattern.

It comes down to internal implementation details of the allocation functions, but one possible explanation is:

  • The free function looks at how much memory has been allocated to the process vs. how much it's actually using, in order to decide whether some memory should be released (for use by other processes).
  • In the course of doing that analysis, free changes some internal state variables used by the allocator, even if no memory is actually released.
  • As a result, future calls to allocation functions behave differently than they would if free hadn't been called.
Wyzard
  • 33,849
  • 3
  • 67
  • 87
  • So you mean that as soon as the first free() is called, it curtails the high memory range (and passes the rest back to the kernel), and then switches to the low memory range and gets new memory from the kernel? – Ralf Dec 13 '14 at 17:49
  • Well, it can't return that high block of memory back to the kernel, because you still have things allocated there. But it can change whatever internal state it uses to decide which block to check first for future allocations. It could be that the low block had free space all along, but it had previously been checking the high block (which also has free space) first. – Wyzard Dec 13 '14 at 17:51
  • 1
    I can add a new observation: If the chunk of memory allocated is smaller (e.g. only 10000 floats instead of 100000), posix_memalign always returns addresses from the low range. I tested different chunk sizes in the first / second loop. The behavior is weird: 100000/100000 -> high/low addresses, 10000/10000 -> low/low addresses, 10000/100000 -> low/high addresses, 100000/1000000 -> high/high addresses. Looks like the MMAP_THRESHOLD mentioned in the malloc man page (128 kB), but I still don't understand the different address ranges and why the behavior switches after the first free(). – Ralf Dec 17 '14 at 15:51