3

I am working on a program where I am using large but limited amount of memory. Memory is allocated and freed on run time on different threads. However, I noticed that the memory usage of the program would not remain within specified bounds. It would increase as time passed on. I wrote the following sample program to check whether memory is being freed back to the OS. Half the allocated memory was freed to check if the memory usage went down.

int main()
{
    char *p[COUNT];

    for(int i = 0; i < COUNT; i++)
    {
        p[i] = new char[1048576];
        memset (p[i], 0, 1048576);
        printf("%p\n", p[i]);
    }

    printf("done allocating ... \n");

    sleep(10);

    printf("Freeing\n");
    for(int i; i < COUNT; i++)
    {
        delete[] p[i];
    }

    while(1)
        sleep(1);
}

After running the program, it seemed that the OS would not reclaim the freed pages. The memory usage remains the same as seen in "top" command in linux, after allocating and after freeing. It simply marks the pages as free for reuse by the same program. In my program, malloc and free are running on different threads. This poses memory management problem when malloc is called a lot more frequently than free and the process data segment grows very large, causing OS to swap pages to disk. This makes the program and OS, slow and unresponsive. Is there any way for the OS to reclaim freed memory ?

callyalater
  • 3,102
  • 8
  • 20
  • 27
user3296247
  • 83
  • 2
  • 8
  • allocate 2000 vs deallocate 1000 ??? –  Mar 11 '14 at 10:35
  • 3
    You are incorrect. Retention of virtual memory does not make either the program or the OS slow and unresponsive. The OS does reclaim freed physical memory. Reclaiming virtual memory is pointless because it is not a scarce resource. – David Schwartz Mar 11 '14 at 10:38
  • @Dieter Lucking, just freeing half the allocated memory to see if the memory usage goes down – user3296247 Mar 11 '14 at 10:39
  • @DavidSchwartz the system indeed becomes slow. I ran the same program as shown above, but this time freeing all the memory. The program runs fast and is able to allocate and free all the memory in 2-3 seconds. However, when I run the same program again, without terminating the previous process,the new process is allocated memory in a very slow fashion. kswap(kernel swapper process) becomes active with high CPU usage.Although OS does reclaims freed memory, it reclaims only when there is need and the process of reclaiming is making the system slow.Can OS to reclaim instantly when free is called? – user3296247 Mar 11 '14 at 10:58
  • 2
    What do you mean by "but this time freeing all the memory"? It's not at all clear what it is you are doing. (Also, your code never sets `i` back to 0 before the second `for` loop.) – David Schwartz Mar 11 '14 at 11:01
  • 1
    You definitely have a memory management problem - that program never frees any memory at all. – molbdnilo Mar 11 '14 at 11:07
  • @DavidSchwartz, in the program above, malloc loops till 2000 and freee() loops till 1000. This time, both loop up to 2000 iterations, hence freeing as much as allocated. However, this program is not terminated after allocating and freeing. Its just allowed to live in the memory and another instance of the same program is run. Also, sleep(1) was added to the body of last "while" loop so that this infinite loop does not hoards CPU. The new instance of the same program runs slow – user3296247 Mar 11 '14 at 11:09
  • I apologize for the old version of the program. Let me add the updated version. – user3296247 Mar 11 '14 at 11:10
  • David Schwartz is right, your second loop will never execute because you don't set i to zero to start with. Better write your for loops as : for(int i = 0; i < 2000; i++) (it's nice to keep the loop variable localized if possible). If you insist on declaring outside the loop then it shoud be : int i; for(i = 0; i < 2000; i++) – Graham Griffiths Mar 11 '14 at 11:15
  • @user3296247 Well, you had a lot of bugs, you might still have more. Not looping correctly was a bug. Not sleeping and thus consuming CPU was a bug. Keep troubleshooting and maybe you'll find more issues. – David Schwartz Mar 11 '14 at 11:18
  • @DavidSchwartz Thank you for finding the small yet significant bug. I was testing this program with malloc_trim(0) and was wondering why it still wasn't releasing the memory. After fixing the loop problem, I was able to release the memory back to OS. :) – user3296247 Mar 11 '14 at 11:23

2 Answers2

4

On Linux, you might get some space (in virtual memory) with mmap(2) (which is used by malloc or ::operator new, etc...). Then you can release later it with munmap

Since mmap and munmapare somehow expensive, malloc (hence ::operator new which is often implemented above malloc) try to reuse previously free-d memory zones, so don't bother to always release memory to the kernel (below a big threshold, perhaps 128K or more).

BTW, proc(5) provides a useful interface to the kernel to query things. For a process of pid 1234, you could cat /proc/1234/maps to show its address space memory map (from inside your process, use /proc/self/maps)

So you could code:

const size_t sz = 1048576;
/// allocating loop
for (i=0; i < 2000 ;i++ ) {
  void* ad = mmap(NULL, sz, PROT_READ|PROT_WRITE, 
                  MMAP_PRIVATE|MMAP_ANONYMOUS,
                  -1, (off_t)0);
  if (ad == MMAP_FAILED)
    { perror("mmap"); exit (EXIT_FAILURE); }
  p[i] = ad;
  memset (p[i], 0, sz); // actually uneeded
}
printf ("done allocating ... \n");
sleep(5);
// freeing loop
printf("Freeing\n");
for (i=0; i < 2000 ;i++ ) {
  if (munmap((void*)p[i], sz)) 
    { perror("munmap"); exit(EXIT_FAILURE); }
  p[i] = nullptr;
}

Notice that mmap with MAP_ANONYMOUS is giving on success a zeroed memory zone, so you don't need the memset to clear it.

In C++ you might also define your own operator new (calling mmap) and operator delete (calling munmap) if so wanted.

Read also Advanced Linux Programming.

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
2

After the process has exited, all memory used by the process will be reclaimed. The OS may keep the data speculatively cached just in case (as explained here on Linux Ate My RAM). The memory will be freed up whenever another process needs it.

EDIT : since you mean a long-running server process, then your concern is memory usage while the process is still running. May I suggest valgrind as a tool to detect memory leaks in your application, and RAII as a coding technique to prevent memory leaks. Careful that memory usage on Linux (and in general!) is difficult to measure / interpret, the output of tools like ps and top can be misleading and unintuitive e.g. as per this answer.

Community
  • 1
  • 1
Graham Griffiths
  • 2,196
  • 1
  • 12
  • 15