4

To test out of memory behaviour, I compiled the following C program on 32-bit Linux 3.2 using GCC 4.7.1 without any compiler flags:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main() {
    while (malloc(4096)) ;
    printf("%s", strerror(errno));
    return 0;
}

When I run the program, I observe that malloc fails ("Cannot allocate memory") after approximately 2.5 GB of resident memory are allocated.

The machine has 2GB of physical memory and 4GB of swap. No kernel message was observed for the duration of program run.

So why did Linux stop giving out memory?

Related question: maximum memory which malloc can allocate, but it doesn't address the Linux specifics.

Community
  • 1
  • 1
  • 1
    You're definitely not gonna get above 4GB. And allocations have overhead. Though I'm not sure if that will make 4GB go down to 2.5GB... – Mysticial Jul 29 '13 at 23:28
  • 3
    32-bit Linux limits processes to 3GB of address space. – Ferruccio Jul 29 '13 at 23:31
  • possible duplicate of [maximum memory which malloc can allocate](http://stackoverflow.com/questions/2798330/maximum-memory-which-malloc-can-allocate) – Derick Leony Jul 29 '13 at 23:32
  • the another question is related to Windows platform, my question is about Linux. –  Jul 29 '13 at 23:33
  • 1
    what do you get after you type `ulimit -a | grep memory` ? – sgun Jul 29 '13 at 23:41
  • @sgun thanks, ulimit -a says unlimited virtual memory and memory size. –  Jul 29 '13 at 23:43
  • 1
    My process was just killed by kernel with "killed" message. Mine is unlimited too. Looks like the latest implementation of kernel gives us so-called unlimited memory and then when page-replacement frequency exceeds a specific limit, it just kills the process responsible for this. As @JaredPar suggests 32bit already limits your address space. But this is not the issue here becase your program is never accessing the allocated memory. – sgun Jul 29 '13 at 23:56
  • @sgun I was expecting to see a kernel oom message too, but it didn't happen in 3.2 .. –  Jul 30 '13 at 00:08
  • 1
    Looks like developers have fixed it after 3.2. Mine is 3.5. – sgun Jul 30 '13 at 00:13
  • How has this question "not received enough attention"?! – Kerrek SB Aug 01 '13 at 21:40
  • @KerrekSB now it has :) –  Aug 01 '13 at 23:13

4 Answers4

9

The amount of physical memory in your machine has no bearing on the semantics of malloc. A process has a fixed virtual address space (usually 2GB for 32 bit processes). The implementation of malloc will return addreses until it runs out of virtual address space not physical RAM.

Here is a much more detailed discussion

http://en.wikipedia.org/wiki/Virtual_address_space

JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • 5
    For 32 bit Linux on x86 the address space is 3 GB for user code. Taking into account address space for text (code), BSS, stack etc. you get roughly to the 2.5 GB limit mentioned. – gby Aug 01 '13 at 06:26
  • 1
    @Krishna no, the amount of memory available to a process is independent of other processes on the system – JaredPar Aug 01 '13 at 18:57
  • 1
    @JaredPar...Thanks friend. – someone Aug 01 '13 at 19:00
  • how can we allocate and check the physical memory from linux ? – ransh Mar 15 '16 at 13:19
4

What you are observing is a VM overflow.

32-bit processes have access to only 4GB of virtual address space. That virtual address space has to accomodate

  • Your process text
  • Text of any shared libraries that you load in (libc, libdl, ...)
  • At least one stack
  • Text for vsyscalls
  • heap
  • any other VM mappings

The OS will normally program the memory controller on your CPU to reserve a (large) part of your VM space for kernel pages (vsyscalls among them) and limit the memory usable by your process to less then those 4GB. This used to be half of the VM, nowadays it's less.

glibc implementation of malloc keeps one (or more) blocks of VM space for your heap, but it is programmed to leave enough VM space for shared mappings. Thus depending on what else is located in your virtual address space you may hit "out of memory" before your full VM is saturated.

Consult /proc/[pid]/maps for locations of your VM mappings.

The only way to use your full VM is to use mmap and family of calls to allocate memory anywhere in your free VM. Try:

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <unistd.h>

int main() {
    char * page;
    size_t memory_allocated = 0;
    while ((page = mmap(NULL, sysconf(_SC_PAGE_SIZE)
                        , PROT_READ | PROT_WRITE
                        , MAP_ANON | MAP_PRIVATE, -1, 0))
                 != MAP_FAILED) {
        memory_allocated += sysconf(_SC_PAGE_SIZE);
        // optionally touch the page
        // otherwise the mapping won't use physical RAM/swap
        // *page = 1; 
    }

    fprintf(stderr, "Allocated %zu MiB\n", memory_allocated >> 20);
    perror("mmap");
    return 0;
}

Please do not try this as 64-bit binary as it will attempt to use up to 256TB of RAM there.

On my Mac OS X i get:

$ gcc -m32 17935873.c
$ ./a.out
Allocated 3516 MiB
mmap: Cannot allocate memory
Sergey L.
  • 21,822
  • 5
  • 49
  • 75
0

Are you running this code with root privilege? Once I also tried memory allocation code with user privilege and I got error.

This is not your answer, But I want to share my sample code here, May be we can learn something from this. With this approach we can allocate required amount of memory until memory running out.Well this is working code (If you tried for more memory, then it will give error).

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#define A_MEGABYTE (1024 * 1024)
#define PHY_MEM_MEGS 1024 /* Adjust this number as required */
int main()
{
    char *some_memory;
    size_t size_to_allocate = A_MEGABYTE;
    int megs_obtained = 0;
    while (megs_obtained < (PHY_MEM_MEGS * 2))
    {
        some_memory = (char *)malloc(size_to_allocate);
        if (some_memory != NULL)
        {
            megs_obtained++;
            sprintf(some_memory, “Hello World”); //Message to check
            printf(“%s - now allocated %d Megabytes\n”, some_memory, megs_obtained);
        }
        else
        {
             exit(EXIT_FAILURE);
        }
    }
    exit(EXIT_SUCCESS);
}

May be this can help.

EDIT

This is upto my understanding, Please let me know if I am wrong.

As you said you are getting 2.5 GB of memory, this is because heap can allocate 2/3 times of its memory. So you will get Max 3 GB from 4 GB of memory. Rest memory occupied by some processes and definitely process scheduler. No intelligent OS allow itself for suicide.

Here I read Operating System book of Galvin. According to the book in your case if you want to allocate more memory just call malloc 1000 times (single line for each malloc call), or upto the Page Size of OS. Until you swap pages in memory you will not get Kernel Message(Theoretically). Your page is not swaped out from memory, this is the only problem.

Between if you want to check all your Swap area write this code with Thread with sleep so you can check what all processes are in swap memory.

I did not write any code for that so please update us if you write some code.

someone
  • 1,638
  • 3
  • 21
  • 36
  • 1
    Different users having different "soft" limits on available virtual memory is nothing new. One can use `ulimit -m` or `ulimit -v` to configure that... – TheCodeArtist Aug 01 '13 at 05:23
0

The memory could be fragmented such that there are several chunks of space less than 4k. The malloc will only return a pointer to the allocated memory when it can find the requested space.

Also, there are embedded pointers in the unallocated storage that points to or links together free areas (per K&R). This is a general statement that is true for Linux and Windows.

In Windows, I have dumped the area around the pointer returned by malloc and found that Windows has about 16 bytes of info (mostly handles I think) prefixing each malloc'd area. These prefixes are NOT returned to the invoker of malloc, but they do take up space and reduce the area available on a specific machine.

You might try stepping down your allocation with a second loop:

   int main()
   {
       int avail = 4096;

       while (malloc(avail)) ;
       printf("%s", strerror(errno));

       avail = avail >> 1;
       while (malloc(avail)) ;
       printf("%s", strerror(errno));

       // etc. down to whatever granularity you want
       // or even better code an inner lup to do this

       return 0;
   }
JackCColeman
  • 3,777
  • 1
  • 15
  • 21