1

I noticed that this program:

#include <stdio.h>

int main() {
  const size_t alloc_size = 1*1024*1024;
  for (size_t i = 0; i < 3; i++) {
    printf("1\n");
    usleep(1000*1000);
    void *p[3];
    for (size_t j = 3; j--; )
      memset(p[j] = malloc(alloc_size),0,alloc_size); // memset for de-virtualize the memory
    usleep(1000*1000);
    printf("2\n");
    free(p[i]);
    p[i] = NULL;
    usleep(1000*1000*4);
    printf("3\n");
    for (size_t j = 3; j--; )
      free(p[j]);
  }
}

which allocates 3 memories, 3 times and each time frees different memory, frees the memory according to watch free -m, which means that the OS reclaimed the memory for every free regardless of the memory's position inside the program's address space. Can I somehow get a guarantee for this effect? Or is there already anything like that (like a rule of >64KB allocations)?

LyingOnTheSky
  • 2,844
  • 1
  • 14
  • 33
  • 3
    Uh....? Yes, unless the OS has a bug. – user202729 Jan 20 '18 at 16:10
  • @user202729 when allocating a single byte and freeing it, the OS won't reclaim that byte. – LyingOnTheSky Jan 20 '18 at 16:11
  • You may print the memory address of the pointer to check if the memory is reused. If the addresses are same, then yes else, no. – kiner_shah Jan 20 '18 at 16:13
  • 3
    Why do you need that guarantee? The OS should reclaim the memory when it absolutely needs to. But, as reclaiming may be an expensive operation it might like to defer it as long as possible – Kninnug Jan 20 '18 at 16:13
  • @kiner_shah what part of this gives me the guarantee? – LyingOnTheSky Jan 20 '18 at 16:14
  • @LyingOnTheSky you can't allocate a single byte. Your process can get a full page (or multiple pages) of more memory mapped into its address space. Is it possible you have no idea how memory segmentation and virtual memory works? – Marcus Müller Jan 20 '18 at 16:15
  • 2
    @kiner_shah that's wrong. What you then can see is whether *within one process*, the same virtual memory **address** is reused. That has nothing to do with the page and even less with the physical memory being reused. – Marcus Müller Jan 20 '18 at 16:16
  • @Kninnug I have a program that must not take more than the user specified to take, and sometimes one component in the program will want to "share" memory from another component by freeing the other component and allocating itself, and I need this guarantee so I won't have the freed and allocated memories both in memory. – LyingOnTheSky Jan 20 '18 at 16:16
  • @LyingOnTheSky, OS won't access a memory address if it is used by some other process (until one attempts buffer overflow). So, the only chance remains is when it is freed by the process which was originally accessing it. And, as you said, the OS will reclaim the memory when it needs. In case of heavy duty process running, OS may run out of memory and may be forced to access the same address if it's available! – kiner_shah Jan 20 '18 at 16:17
  • @MarcusMüller, indeed, yes! I was talking in context of virtual memory! In context of physical memory, as others said, its uncertain whether the OS will reclaim any freed memory! – kiner_shah Jan 20 '18 at 16:19
  • 1
    @kiner_shah sorry, you still are way off. Addresses within an address space is something else than the memory being mapped there. – Marcus Müller Jan 20 '18 at 16:21
  • @LyingOnTheSky instead of freeing and re-allocating, why not pass the pointer to memory from one component to another? If they are in the same process, they can freely exchange pointers. If you're on a platform with little memory (e.g. an embedded device) and a specific OS, it might have additional APIs to manage memory more strictly (e.g. [mmap](http://man7.org/linux/man-pages/man2/mmap.2.html) on POSIX systems). – Kninnug Jan 20 '18 at 16:29
  • @Kninnug what if I need 1G, I have 0.5G free, I will take 1G from the other component? doesn't sounds effective. Plus it's a lot of code making an allocator that can make other components take pointers that fit the best and etc. – LyingOnTheSky Jan 20 '18 at 16:32
  • 1
    [Will malloc implementations return free-ed memory back to the system?](https://stackoverflow.com/q/2215259/608639), [Memory not freed after calling free()](https://stackoverflow.com/q/5365996/608639), [Force free() to return malloc memory back to OS](https://stackoverflow.com/q/27945855/608639), etc. – jww Jan 20 '18 at 21:38
  • Possible duplicate of [Force free() to return malloc memory back to OS](https://stackoverflow.com/questions/27945855/force-free-to-return-malloc-memory-back-to-os) – jww Jan 20 '18 at 21:40

4 Answers4

4

The short answer is: In general, you cannot guarantee that the OS will reclaim the freed memory, but there may be an OS specific way to do it or a better way to ensure such behavior.

The long answer:

  • Your code has undefined behavior: there is an extra free(p[i]); after the printf("2\n"); which accesses beyond the end of the p array.

  • You allocate large blocks (1 MB) for which your library makes individual system calls (for example mmap in linux systems), and free releases these blocks to the OS, hence the observed behavior.

  • Various OSes are likely to implement such behavior for a system specific threshold (typically 128KB), but the C standard gives guarantee about this, so relying on such behavior is system specific.

  • Read the manual page for malloc() on your system to see if this behavior can be controlled. For example, the C library on Linux uses an environment variable MMAP_THRESHOLD to override the default setting for this threshold.

  • If you program to a Posix target, you might want to use mmap() directly instead of malloc to guarantee that the memory is returned to the system once deallocated with munmap(). Note that the block returned by mmap() will have been initialized to all bits zero before the first access, so you may avoid such explicit initialization to take advantage of on demand paging, on perform explicit initialization to ensure the memory is mapped to try and minimize latency in later operations.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • why? it's not an allocator thing that tells the OS that X-Y address can be reclaimed? at least say something that convinces that it's an OS thing. – LyingOnTheSky Jan 20 '18 at 16:12
  • Thanks for the update, it sounds like allocating with mmap and munmap can help me, no? You unintentionally implied that. – LyingOnTheSky Jan 20 '18 at 16:18
  • 2
    well, depends on what you mean with "help you": you still haven't said what your *problem* is, and frankly, all this reeks of the [XY Problem](http://xyproblem.info) **extremely much**. So, please edit your question and explain why you think an answer is relevant to the problem you're solving, and what that problem actually is. – Marcus Müller Jan 20 '18 at 16:23
  • Sorry for the multiple updates, I was trying to answer promptly, but this question required a detailed answer. – chqrlie Jan 20 '18 at 16:23
  • @MarcusMüller I did in the comments " I have a program that must not take more than the user specified to take, and sometimes one component in the program will want to "share" memory from another component by freeing the other component and allocating itself, and I need this guarantee so I won't have the freed and allocated memories both in memory. " – LyingOnTheSky Jan 20 '18 at 16:24
  • aaahh, OK. So your actual question has nothing to do with guarantee reuse of memory, it's actually about guaranteeing being limited in memory usage. That's a very different question. – Marcus Müller Jan 20 '18 at 16:29
  • @MarcusMüller it's OS memory reclamation, not memory reuse. currently `mmap` matches perfectly my case, do you think that if I phrased my question like I did in the last comment, then I will get better answer than this? – LyingOnTheSky Jan 20 '18 at 16:35
1

On the OSes I know, and especially on linux:

no, you cannot guarantee reuse. Why would you want that? Reuse only happens when someone needs more memory pages, and Linux will then have to pick pages that aren't currently mapped to a process; if these run out, you'll get into swapping. And: you can't make your OS do something that is none of your processes' business. How it internally manages memory allocations is none of the freeing process' business. In fact, that's security-wise a good thing.

What you can do is not only freeing the memory (which might leave it allocated to your process, handled by your libc, for later mallocs), but actually giving it back (man sbrk, man munmap, have fun). That's not something you'd usually do.

Also: this is yet another instantiation of "help, linux ate my RAM"... you misinterpret what free tells you.

Marcus Müller
  • 34,677
  • 4
  • 53
  • 94
1

For glibc malloc(), read the man 3 malloc man page.

In short, smaller allocations use memory provided by sbrk() to extend the data segment; this is not returned to the OS. Larger allocations (typically 132 KiB or more; you can use MMAP_THRESHOLD on glibc to change the limit) use mmap() to allocate anonymous memory pages (but also include memory allocation bookkeeping on those pages), and when freed, these are usually immediately returned to the OS.

The only case when you should worry about the process returning memory to the OS in a timely manner, is if you have a long-running process, that temporarily does a very large allocation, running on an embedded or otherwise memory-constrained device. Why? Because this stuff has been done in C successfully for decades, and the C library and the OS kernel do handle these cases just fine. It just isn't a practical problem in normal circumstances. You only need to worry about it, if you know it is a practical problem; and it won't be a practical problem except on very specific circumstances.


I personally do routinely use mmap(2) in Linux to map pages for huge data sets. Here, "huge" means "too large to fit in RAM and swap".

Most common case is when I have a truly huge binary data set. Then, I create a (sparse) backing file of suitable size, and memory-map that file. Years ago, in another forum, I showed an example of how to do this with a terabyte data set -- yes, 1,099,511,627,776 bytes -- of which only 250 megabytes or so was actually manipulated in that example, to keep the data file small. The key here in this approach is to use MAP_SHARED | MAP_NORESERVE to ensure the kernel does not use swap memory for this dataset (because it would be insufficient, and fail), but use the file backing directly. We can use madvise() to inform the kernel of our probable access patterns as an optimization, but in most cases it does not have that big of an effect (as the kernel heuristics do a pretty good job of it anyway). We can also use msync() to ensure certain parts are written to storage. (There are certain effects that has wrt. other processes that read the file backing the mapping, especially depending on whether they read it normally, or use options like O_DIRECT; and if shared over NFS or similar, wrt. processes reading the file remotely. It all goes quite complicated very quickly.)

If you do decide to use mmap() to acquire anonymous memory pages, do note that you need to keep track of both the pointer and the length (length being a multiple of page size, sysconf(_SC_PAGESIZE)), so that you can release the mapping later using munmap(). Obviously, this is then completely separate from normal memory allocation (malloc(), calloc(), free()); but unless you try to use specific addresses, the two will not interfere with each other.

Nominal Animal
  • 38,216
  • 5
  • 59
  • 86
0

If you want memory to be reclaimed by the operating system you need to use operating system services to allocate the memory (which be allocated in pages). Deallocate the memory, you need to call the operating system services that remove pages from your process.

Unless you write your own malloc/free that does this, you are never going to be able to accomplish your goal with off-the-shelf library functions.

user3344003
  • 20,574
  • 3
  • 26
  • 62
  • Um, no offence, but what would a libc's malloc/free do aside from a) keeping an internal memory pool and b) using the operating system's functionality to get more memory (and release it back to the OS)? – Marcus Müller Jan 20 '18 at 16:27
  • libc's malloc/free will allocate pages to the process. Last I checked it never deallocates pages but, instead retains them for future allocations. – user3344003 Jan 20 '18 at 16:32
  • huh, glibc source code: `malloc/malloc.c`, function `munmap_chunk` should be called by `__libc_free` whenever a chunk that was previous `mmap`ed gets freed. – Marcus Müller Jan 20 '18 at 16:39