18

I'm writing an application which needs a lot of memory for caching purposes as I described he here. Now I'm playing around with some malloc / new constructions to figure out how I could realise it. I made a strange observation:

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

int main(void) {
  while(1) {
    char *foo = (char*)malloc(1024);// new char[1024];
    if(foo == NULL) { 
      printf("Couldn't alloc\n");
      fflush(stdout);
      return 0;
    }
  }
  return 0;
}

Why does that printf never be reached? If my system runs out of memory, malloc is said to return NULL, as it is explained here. But I always receive SIGKILL (I'm using linux...).

Community
  • 1
  • 1
rralf
  • 1,202
  • 15
  • 27
  • 1
    Sigkill indicates that your Scheduler is killing off the program. – Bjoern Rennhak May 21 '13 at 16:01
  • 2
    @slugonamission: Overcommit/OOM killer was my first thought too, but the OP is _not_ dereferencing the pointer, so there's actually no reason for a crash (or SIGKILL). – Damon May 21 '13 at 16:06
  • @Damon - I noticed that too, so I'm not too sure why it would fail in that circumstance. I just posted it as more of an idea really. – slugonamission May 21 '13 at 16:07
  • 2
    @Damon But are you sure the heap implementation isn't trying to access the memory returned by the kernel? – Captain Obvlious May 21 '13 at 16:08
  • 2
    please don't cast the result of malloc – KevinDTimm May 21 '13 at 16:09
  • @CaptainObvlious: `calloc` sure would, but `malloc` _should not_, since memory allocated by `malloc` is not initialized (though of course it _might_, who knows). – Damon May 21 '13 at 16:10
  • Then my compiler will fail (-fpermissive) – rralf May 21 '13 at 16:10
  • 2
    The cast of malloc tends to mask other problems in your code. Since the return is guaranteed to be aligned properly for the type of data requested, this masking is not desirable. – KevinDTimm May 21 '13 at 16:12
  • 1
    @Damon: while the visible code is not accessing the allocated memory, it's a fair bet that `malloc()` has to keep its book-keeping information somewhere and does access the memory to do so, triggering the OOM. – Jonathan Leffler May 21 '13 at 16:12
  • 1
    Since you also ask after `new`: vanilla `new` will never return `nullptr` because its specs say so. It’ll throw instead (but on Linux, that won’t happen either, see answers). – Konrad Rudolph May 21 '13 at 16:13
  • I believe `malloc` will definitely return null on Linux if the highest bit is set, like `malloc(-1)`. – Kerrek SB May 21 '13 at 16:18
  • 4
    On a more humorous note, there's an analogy of OOM killer [here](http://lwn.net/Articles/104185/)... – autistic May 21 '13 at 16:41
  • 2
    I would still expect a null pointer from malloc once the address space is exhausted, which is feasible on a 32-bit system with lots of RAM. – Adrian McCarthy May 21 '13 at 17:33
  • @Damon: I updated my response to address that issue. The overcommit policy is the reason why malloc never returns NULL, the malloc overhead is likely the reason the memory still eventually fills up and invokes OOM doom. :-) I might guess also that overcommit might allocate some fraction of what you request, but I haven't found any sources to confirm this. – Anthony May 21 '13 at 19:41
  • 1
    I'm honestly shocked by this design as it breaks basic assumptions. Hell this definitely breaks the guarantees of malloc that are required by the Standard [§7.20.3.3.3]... – MFH May 21 '13 at 23:26
  • @Damon, @CaptainObvlious and others: Normally, we could `malloc()` until the address space is exhausted. Then we get `NULL`. The reason why the OOM killer gets active here is the small `malloc()` size of 1k, smaller than 1 page. The book keeping happens "inline", making every allocated page dirty. If we would `malloc()` larger chunks, only a few pages would get dirty and OOM wouldn't get active. – glglgl May 22 '13 at 07:22

4 Answers4

25

Linux, by default, usually uses an opportunistic memory allocation scheme, meaning the kernel will give you a valid address that won't be allocated until first use.

See:

According to those responses you can turn this feature off using echo 2 > /proc/sys/vm/overcommit_memory.

From what I can tell, this is done under the assumption that you wont necessarily use all the memory that you allocate. I can't say that I personally ever allocate space that I don't touch at least once, so I'd be curious to know how this affects real life performance...

Regarding the SIGKILL failure, every malloc you call is still allocating some memory for each call. Eventually you will likely fill your memory with malloc overhead and thus evoke the fury of the out of memory kill feature. Whether this alone is the issue or if perhaps the overcommit policy still allocates some fraction of the requested space is a good question.

Community
  • 1
  • 1
Anthony
  • 2,256
  • 2
  • 20
  • 36
  • 1
    Ah okay, I think this answers my question. But to be honest - this is really strange. If this is the default setting on almost every linux system, why should I check the result of malloc for being NULL.... – rralf May 21 '13 at 16:14
  • @rraif maybe you can still fail when requesting an insanely large chunk of buffer.... – phoeagon May 21 '13 at 16:16
  • 7
    @rralf: Because not every system is Linux, and not every Linux system uses the default settings, and there's no guarantee that it won't return NULL under the default settings. – Mike Seymour May 21 '13 at 16:18
  • 3
    @rralf Because perhaps tomorrow Linux will be modified and this "feature" removed? Because maybe your program will run on a system with `overcommit_memory` set to `2`? And so on. TL;DR: Because it's the **right** thing to do. – Nik Bougalis May 21 '13 at 16:22
  • @NikBougalis: Yes of course, I know :) But I would like my process to recognize when my system runs out of memory. If malloc would return null, this would solve my problem. Otherwise I have to do lots of workarounds to realise it. One cannot necessarily assume that my program runs on a system with overcommit_memory=2 – rralf May 21 '13 at 16:25
  • The kernel is not assuming that you wont use the memory at all, it simply assumes that you aren't going to use it *right now*. A bit like paging on demand and copy on write; they assume you aren't going to read/write immediately and hence avoid early memory access. – Bakuriu May 21 '13 at 17:05
  • 1
    @Bakuriu: From what I've read thus far, it seems it *is* implemented assuming you won't use all the memory you allocate. If it thinks you will use it (now or later) then it doesn't make much sense to approve the over-allocation unless the kernel expects other processes to free memory between now and then. It does seem useful for process forking which "theoretically" duplicates the parent's memory, but if the forked child never touches that memory then you never have to duplicate it at all. That's what I've gleaned from what I've read so far anyway, but I haven't researched it too extensively. – Anthony May 21 '13 at 17:14
  • 3
    @Anthony: an example is allocating a huge 1GB temp working area like some VMs and server processes do. You definitely won't be using it all at once. You may only need a few hundred MB of physically-backed memory at any time, with the rest being unused or able to be efficiently moved to swap. Do note that Linux cannot guarantee to _never_ return NULL; there is an upper bound to the address space and it can't "fake" anything if you exhaust all of that. – Sean Middleditch May 21 '13 at 17:29
  • @SeanMiddleditch: That's an interesting application although, to me at least, it seems like it's compensating for sub-par design practices. Considering stability, I would think the VM and server processes should be able to handle the case where there is not enough virtual memory instead of crossing their fingers when actually trying to use that 1GB space and then either crashing or causing something else to crash. But maybe that's just the mindset you get when you work in embedded systems too long... :) – Anthony May 21 '13 at 18:12
  • @Anthony Think of the efficiency, then. What's better, 10,000 requests to `malloc(100)`, or 1 request to `malloc(100 * 10000)`? `malloc` is a system call, and there's going to be overhead in each request. Do them often enough and the user might start to feel the choppiness, especially in a VM... – Izkata May 21 '13 at 18:30
  • @Izkata: I get that it's more efficient to allocate larger chunks less frequently but the application could just as well allocate its own "pages" as it needs them (e.g. allocate in 10MB blocks or so as needed). This way the application can remain efficient while not hording all the available memory (or being promised memory that doesn't exist). At the same time, the application can now handle the event where there is no more memory instead of just crashing. For server applications especially this route seems more logical and stable. But clearly it must not be a big deal if it's the default. :) – Anthony May 21 '13 at 18:45
  • 1
    @Anthony Those 10MB "pages" _are_ the pre-allocation this setting is for. You aren't necessarily going to use the whole 10MB, so it only gets allocated as it's accessed. ;) – Izkata May 21 '13 at 18:57
  • 1
    @Izkata: My point is regarding stability: if the kernel tells the process it has 1GB (or 10MB for that matter) allocated to it but there's really not that much memory left, the application has no idea, so if it actually tries to use that space it risks crashing itself (or other processes) due to insufficient memory because of over-allocation. Whereas if the application requests 10MB but the space is not there and the kernel returns a NULL pointer, the application can handle this scenario gracefully. In the former scenario we cross our fingers and hope for the best. The latter is better design. – Anthony May 21 '13 at 19:06
  • Linux is a severe design error. – Elmue Apr 18 '18 at 16:11
6

Usually, Linux will allocate as much (virtual) memory as you request, and only allocate physical memory for it when it's needed. If the system runs out of physical memory, then it starts killing processes to free some.

This means that malloc will succeed unless the request is ludicrous, but your process (or some other) is likely to get killed as the memory is used.

For more details, see the manpage for malloc, and its references:

By default, Linux follows an optimistic memory allocation strategy. This means that when malloc() returns non-NULL there is no guarantee that the memory really is available. In case it turns out that the system is out of memory, one or more processes will be killed by the OOM killer. For more information, see the description of /proc/sys/vm/overcommit_memory and /proc/sys/vm/oom_adj in proc(5), and the kernel source file Documentation/vm/overcommit-accounting.

(And of course new won't return null anyway unless you use the no-throwing version).

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • What a stupid misdesign. And how can a programmer detect if the requested memory is available? It is incredibly stupid to kill my process instead of simply returning NULL from malloc() if the memory is not availabe. How can I display an error message to the user if I'am out of memory? I tested the above code from rralf on Windows where it works perfectly! No crash, no killing. Just returning NULL when there is no memory left. Shame on Linux! – Elmue Apr 18 '18 at 16:05
4

malloc returns NULL if requested allocation cannot be fulfilled. But maybe you should try allocating tons of space from heap.

See here.

On linux, the only way to get an error when calling malloc() is to disable memory-overcommiting. On regular linux systems, this is the only way for malloc() to return NULL. If an icecast process reaches that point, it's screwed anyway it won't be able to do anything meaningful: any source reads will fail (refbuf alloc), any log print will also fail (printf uses malloc too), so it might as well give up and call abort().

phoeagon
  • 2,080
  • 17
  • 20
  • Yes, this happens when my system runs out of memory?! But instead of returning NULL, my process gets killed. And.. Err.. malloc allocates space from heap?! – rralf May 21 '13 at 16:02
  • 1
    malloc *does* allocate from the heap... – slugonamission May 21 '13 at 16:02
  • 1
    The OP is wondering they they get SIGKILL instead of `malloc` returning NULL. – Captain Obvlious May 21 '13 at 16:02
  • @CaptainObvlious I know that and I have edited my answer. – phoeagon May 21 '13 at 16:03
  • @phoeagon: Yes, now it's a bit more understandable. But this was a post on some mailing list. Is it specified anywhere? If this would be really like this, then why should I check if my malloc returned an valid value, when it never returns NULL? – rralf May 21 '13 at 16:08
  • @rraif First, read this (http://stackoverflow.com/questions/10420280/malloc-conditions-for-failure), which is about the same topic ... – phoeagon May 21 '13 at 16:11
  • What about heap fragmentation? Couldn't that be a problem, especially on 32-bit where you can allocate 2G of individual bytes, but not 2G continuously? – Earlz May 21 '13 at 16:13
-1

Malloc would return NULL, if the operating system let your program run that long. But before malloc gets a chance to run the operating system kills your process.

Just like it kills your process if it detected that you were writing outside memory pages allocated to your process.

Rafael Baptista
  • 11,181
  • 5
  • 39
  • 59