C Program on Linux to exhaust memory
Asked Answered
T

10

23

I would like to write a program to consume all the memory available to understand the outcome. I've heard that linux starts killing the processes once it is unable to allocate the memory.

Can anyone help me with such a program.

I have written the following, but the memory doesn't seem to get exhausted:

#include <stdlib.h>

int main()
{
        while(1)
        {
                malloc(1024*1024);
        }
        return 0;
}
Textbook answered 8/12, 2009 at 8:26 Comment(2)
If you start the app in one terminal window and watch memory usage in top (type top in the terminal window and then press 'm'), what happens?Icehouse
The memory used by my test program is only 1-2%Textbook
I
15

You should write to the allocated blocks. If you just ask for memory, linux might just hand out a reservation for memory, but nothing will be allocated until the memory is accessed.

int main()
{
        while(1)
        {
                void *m = malloc(1024*1024);
                memset(m,0,1024*1024);
        }
        return 0;
}

You really only need to write 1 byte on every page (4096 bytes on x86 normally) though.

Impend answered 8/12, 2009 at 8:35 Comment(4)
Thanks. The memory decreases on the system to a certain extent. After that the program crashes(segfault)Textbook
@Mark: That is very probably because malloc() returns NULL as it fails to allocate more memory, and you can't write to NULL.Alphonse
In other words, given youtube.com/watch?v=A7uvttu8ct0, your program is Jerry, and Linux is the woman at the car rental service.Lysenko
@Impend How can this code be updated to support the amount of memory I want to consume as a parameter from standard input, and the period of time it should stay allocated?Referent
I
10

Linux "over commits" memory. This means that physical memory is only given to a process when the process first tries to access it, not when the malloc is first executed. To disable this behavior, do the following (as root):

echo 2 > /proc/sys/vm/overcommit_memory

Then try running your program.

Insulin answered 8/12, 2009 at 8:31 Comment(2)
Thanks for the info. I did that. but still the memory usage is only 1-2%.Textbook
You need to write to a page, otherwise it won't be mapped to physical memory.Floriculture
H
5

Linux uses, by default, what I like to call "opportunistic allocation". This is based on the observation that a number of real programs allocate more memory than they actually use. Linux uses this to fit a bit more stuff into memory: it only allocates a memory page when it is used, not when it's allocated with malloc (or mmap or sbrk).

You may have more success if you do something like this inside your loop:

memset(malloc(1024*1024L), 'w', 1024*1024L);
Humphreys answered 8/12, 2009 at 8:33 Comment(1)
Thanks. I got segfault. I understand that malloc fails and memset tries to access memory which wasn't allocated and hence segfaults.Textbook
A
4

In my machine, with an appropriate gb value, the following code used 100% of the memory, and even got memory into the swap. You can see that you need to write only one byte in each page: memset(m, 0, 1);, If you change the page size: #define PAGE_SZ (1<<12) to a bigger page size: #define PAGE_SZ (1<<13) then you won't be writing to all the pages you allocated, thus you can see in top that the memory consumption of the program goes down.

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

#define PAGE_SZ (1<<12)

int main() {
    int i;
    int gb = 2; // memory to consume in GB

    for (i = 0; i < ((unsigned long)gb<<30)/PAGE_SZ ; ++i) {
        void *m = malloc(PAGE_SZ);
        if (!m)
            break;
        memset(m, 0, 1);
    }
    printf("allocated %lu MB\n", ((unsigned long)i*PAGE_SZ)>>20);
    getchar();
    return 0;
}
Angeles answered 7/7, 2011 at 21:28 Comment(0)
F
3

A little known fact (though it is well documented) - you can (as root) prevent the OOM killer from claiming your process (or any other process) as one of its victims. Here is a snippet from something directly out of my editor, where I am (based on configuration data) locking all allocated memory to avoid being paged out and (optionally) telling the OOM killer not to bother me:

static int set_priority(nex_payload_t *p)
{
    struct sched_param sched;
    int maxpri, minpri;
    FILE *fp;
    int no_oom = -17;

    if (p->cfg.lock_memory)
        mlockall(MCL_CURRENT | MCL_FUTURE);

    if (p->cfg.prevent_oom) {
        fp = fopen("/proc/self/oom_adj", "w");
        if (fp) {
            /* Don't OOM me, Bro! */
            fprintf(fp, "%d", no_oom);
            fclose(fp);
        }
    }

I'm not showing what I'm doing with scheduler parameters as its not relevant to the question.

This will prevent the OOM killer from getting your process before it has a chance to produce the (in this case) desired effect. You will also, in effect, force most other processes to disk.

So, in short, to see fireworks really quickly...

  1. Tell the OOM killer not to bother you
  2. Lock your memory
  3. Allocate and initialize (zero out) blocks in a never ending loop, or until malloc() fails

Be sure to look at ulimit as well, and run your tests as root.

The code I showed is part of a daemon that simply can not fail, it runs at a very high weight (selectively using the RR or FIFO scheduler) and can not (ever) be paged out.

Fayum answered 9/12, 2009 at 0:42 Comment(2)
As Raymond Chen likes to say, once you run on root you are on the other side of the airtight hatchway and you're already pwned. I really hope the kernel doesn't let non-root processes lower their oom_adj stat, just like they can't lower their nice.Janice
@Janice No, you have to be root (but also must be root to adjust system paging & swap settings, etc). It's possible to write a rogue program to eat all memory, but you must be UID 0 for it to run effectively, whichever way you go.Fayum
M
2

Have a look at this program. When there is no longer enough memory malloc starts returning 0

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

int main()
{
  while(1)
  {
    printf("malloc %d\n", (int)malloc(1024*1024));
  }
  return 0;
}
Milkfish answered 8/12, 2009 at 11:13 Comment(0)
E
1

On a 32-bit Linux system, the maximum that a single process can allocate in its address space is approximately 3Gb.

This means that it is unlikely that you'll exhaust the memory with a single process.

On the other hand, on 64-bit machine you can allocate as much as you like.

As others have noted, it is also necessary to initialise the memory otherwise it does not actually consume pages.

malloc will start giving an error if EITHER the OS has no virtual memory left OR the process is out of address space (or has insufficient to satisfy the requested allocation).

Linux's VM overcommit also affects exactly when this is and what happens, as others have noted.

Else answered 8/12, 2009 at 13:2 Comment(0)
T
1

I just exexuted @John La Rooy's snippet:

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

int main()
{
  while(1)
  {
    printf("malloc %d\n", (int)malloc(1024*1024));
  }
  return 0;
}

but it exhausted my memory very fast and cause the system hanged so that I had to restart it.

So I recommend you change some code.

For example: On my ubuntu 16.04 LTS the code below takes about 1.5 GB ram, physical memory consumption raised from 1.8 GB to 3.3 GB after executing and go down back to 1.8GiB after finishing execution.Though it looks like I have allocate 300GiB ram in the code.

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

int main()
{
  while(int i<300000)
  {
    printf("malloc %p\n", malloc(1024*1024));
    i += 1;
  }
  return 0;
}

When index i is less then 100000(ie, allocate less than 100 GB), either physical or virtual memory are just very slightly used(less then 100MB), I don't know why, may be there is something to do with virtual memory.

One thing interesting is that when the physical memory begins to shrink, the addresses malloc() returns definitely changes, see picture link below.

I used malloc() and calloc(), seems that they behave similarily in occupying physical memory.

memory address number changes from 48 bits to 28 bits when physical memory begins shrinking

Thisbee answered 29/5, 2017 at 7:0 Comment(1)
If you use the proper format specifier (e.g. '%p') to print the pointer address, you eliminate the need for a cast. There is never a need to cast the return of malloc. See: Do I cast the result of malloc? for thorough explanation.Bazluke
Q
0

I was bored once and did this. Got this to eat up all memory and needed to force a reboot to get it working again.

#include <stdlib.h>
#include <unistd.h>

int main(int argc, char** argv)
{
    while(1)
    {
        malloc(1024 * 4);
        fork();
    }
}
Qualm answered 8/12, 2009 at 8:34 Comment(2)
Why is that if I run the program mentioned above the machine hangs, but if I use the memory allocated my malloc, the program gets segfault?Textbook
It's likely that this is not exhausting memory, since it's not doing anything with the memory it's allocating (see above comments on optimistic memory allocation/use), but rather acting as a normal fork-bomb and exhausting the system's ability to context-switch.Abdomen
Z
0

If all you need is to stress the system, then there is stress tool, which does exactly what you want. It's available as a package for most distros.

Zyrian answered 19/5, 2015 at 1:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.