[HN Gopher] In C, how do you know if the dynamic allocation succ...
___________________________________________________________________
 
In C, how do you know if the dynamic allocation succeeded?
 
Author : ibobev
Score  : 40 points
Date   : 2021-11-05 19:22 UTC (3 hours ago)
 
web link (lemire.me)
w3m dump (lemire.me)
 
| dekhn wrote:
| I once complained about malloc happily allocating memory that the
| physical memory system couldn't satisfy (never let your hand
| write a check your ass can't cash?) but the more experienced
| programmer asked me if I'd heard of fractional reserve banking,
| and if not, whether it bothered me too.
 
  | salawat wrote:
  | If you're smart, the answer is yes, over reliance on
  | statistical multiplexing scares the shit out of you, because
  | it's all fun and games till the check is due.
 
  | pjc50 wrote:
  | Swap space is the Federal Reserve of memory allocation.
 
    | [deleted]
 
  | lmilcin wrote:
  | Not the same thing.
  | 
  | malloc() can tell everybody it has the memory but when push
  | comes to shove the OS will have to admit overbooking.
 
    | underdeserver wrote:
    | TIL, unless you explicitly disable memory overcommit, it can
    | and will overcommit.
    | 
    | This is crazy to me.
 
      | lmilcin wrote:
      | It is not. It is actually very useful and advantageous for
      | many reasons.
      | 
      | The disadvantage is the pretty shitty failure mode.
      | 
      | But in practice, I have never seen an operating system that
      | was not a toy that was able to reliably, gracefully handle
      | out of memory condition.
 
      | dekhn wrote:
      | So, uh, how do you feel about fractional reserve banking?
      | Nearly all banks worldwide practice it. Statistically, it's
      | not impossible that the entire world financial system could
      | collapse due to uncorrelated bank runs.
 
        | _3u10 wrote:
        | It is impossible. Only a moron of magnificent magnitude
        | would fail to print additional cash to cover the run.
        | 
        | The problems caused by the feds failure to lend to First
        | Bank of America during the Great Depression are well
        | understood by the central banks.
        | 
        | What would likely happen is the overnight rate would go
        | up to 12%, and additional money would be printed to cover
        | withdrawals for the month or two most people would be
        | willing to forgo 12% interest in a potentially
        | inflationary economy.
 
    | Spivak wrote:
    | That's exactly what fractional reserve banking is.
 
      | lmilcin wrote:
      | Funny thing, you are right. I was thinking about something
      | else I guess.
 
    | zeusk wrote:
    | What do you think is a bank run?
 
  | q-big wrote:
  | > I once complained about malloc happily allocating memory that
  | the physical memory system couldn't satisfy (never let your
  | hand write a check your ass can't cash?) but the more
  | experienced programmer asked me if I'd heard of fractional
  | reserve banking, and if not, whether it bothered me too.
  | 
  | What if you are worried about both? ;-)
 
| andi999 wrote:
| This has nothing to do with C. The problem/feature is with the
| systemcall that does the allocation, and any language has to use
| it.
 
  | baktubi wrote:
  | I pooped and scooped the poop said the Malloy to me be free.
  | Along came the scoop and scoop it went. Free it did. In the
  | bucket it tumbled for the others to enjoy.
 
  | jimbob45 wrote:
  | Is your issue that the system call doesn't return enough
  | diagnostic information? If so, how would you have done it
  | differently? I'm asking out of curiosity, not out of a
  | reflexive instinct to defend C (which has many problems).
 
    | joosters wrote:
    | The difficulty is that the lack of memory might be discovered
    | days after it was allocated (an over-committing allocator
    | simply doesn't know at the time whether there will be memory
    | or not) - how do you asynchronously tell the program that
    | this allocation has now failed in a high-level way?
    | 
    | Generally, UNIX will let you know about the missing memory by
    | sending the process a signal. But by that point, there's not
    | much that can be done to fix things up - and remember, all
    | the fix up code would have to run without allocating any more
    | memory itself. That's extremely tricky in C, and nigh-on
    | impossible in other languages.
 
| not2b wrote:
| On Linux, people should be pointed to
| 
| https://www.kernel.org/doc/Documentation/vm/overcommit-accou...
 
| DougN7 wrote:
| Is this really an issue with C? Isn't it ultimately an OS config
| issue?
 
| hdjjhhvvhga wrote:
| This not "In C", this is a system-related issue. I always disable
| overcommit as I see no benefit on my systems.
 
  | Arnavion wrote:
  | I used to disable overcommit, but I ran into a bunch of
  | compilers that would crash because of it despite having 30GiB
  | of free (as reported by free(1) ) memory available.
 
| lmilcin wrote:
| One of my interview questions starts with "Can a program allocate
| more memory than is physically available on the server?"
| 
| Everybody gets this wrong (which is funny for a binary question)
| but it starts an interesting discussion through which I hope to
| learn how much they know about OS and virtual memory.
 
  | floxy wrote:
  | Don't leave us hanging. The "obvious" answer would seem to be
  | "yes" because of swap. But if everyone gets that wrong...
 
    | yjftsjthsd-h wrote:
    | No, it's worse than that - the answer is "yes", because
    | virtual memory + overcommit means that most of the time the
    | OS will happily allow you to allocate more memory than
    | physical+swap, and essentially gamble that you won't actually
    | need all of it (and this is implemented because apparently
    | that's almost always a winning bet).
 
      | lmilcin wrote:
      | Yeah. And the issue is that the actual problem happens
      | sometime later when the application actually tries to use
      | that memory. So you replaced an error that is relatively
      | simple to handle with something that is impossible to
      | handle reliably.
      | 
      | So the operating system very much doesn't like to admit it
      | doesn't have physical memory to back the area you are
      | trying to use. Now it does not have a simple way to signal
      | this to the application (there is no longer an option to
      | return an error code) and so either everything slows down
      | (as OS hopes that another process will return a little bit
      | of memory to get things going for a little while) or one of
      | the processes gets killed.
 
      | OldHand2018 wrote:
      | Processes come and go!
      | 
      | The OS doesn't have to gamble that you won't actually need
      | all the memory you allocate, it could just be a gamble that
      | another memory hogging process exits before you need to use
      | _all_ of your memory, or that you don 't need to use _all_
      | of your memory _at the same time_.
 
| haxorrr666 wrote:
| Nope. Works fine on Fedora 34:
# free -h total used free | shared buff/cache available Mem: 31Gi 3.1Gi 2.2Gi 27Mi 25Gi 27Gi | Swap: 15Gi 62Mi 15Gi # uname -a Linux athena | 5.13.19-200.fc34.x86_64 #1 SMP Sat Sep 18 16:32:24 UTC 2021 | x86_64 x86_64 x86_64 GNU/Linux # gcc -o memaloc memaloc.c # | ./memaloc error! | mirashii wrote: | I'm a little disappointed that the article didn't answer the | question, or at least try to. A discussion of using read/write vs | mincore vs trying to catch a SIGSEGV would've been a nice | addition. | kibwen wrote: | The answer is that you kind of can't. You're at the mercy of | the OS to give you accurate information, and malloc as an | interface isn't set up to distinguish between virtual memory | and "actual" memory. We could imagine a separate interface that | would allow the OS to communicate this distinction (or hacks | like you allude to), but I don't know of any standard approach. | convolvatron wrote: | before linux - every unix OS would fail a page allocation if | there wasn't a backing store. full stop. | | this worked really well | davidw wrote: | This is why some embedded systems don't do malloc (IIRC...been a | while since I've read much about those). | edwinbalani wrote: | That's right - some "safe" coding standards like MISRA C go as | far as forbidding use of malloc() without explicit | justification. | | If you still need dynamic allocation, you might choose to have | a custom allocator working from a fixed-size pool or arena | created in code (possibly itself carved out of RAM with | malloc(), but importantly only once at first setup, where you | have a better guarantee that the allocation will succeed). | edwinbalani wrote: | (All that said, embedded systems sometimes don't have virtual | memory, so the original problem stated in the link is just | not a thing...) | | > working from a fixed-size pool or arena created in code | (possibly itself carved out of RAM with malloc(), but | importantly only once at first setup, where you have a better | guarantee that the allocation will succeed) | | And I should add to this that you probably want to access all | of the pool/arena to do setup, or just ensure it's physically | allocated if you _are_ running in a virtual memory space. | This is something that is reasonable at setup time, though. | stefan_ wrote: | Embedded systems (the ones where you would disallow malloc) | don't generally have virtual memory by virtue of having no MMU, | so on those you can't do overcommitment since there is no page | fault mechanism. | | No, the reason is simply that by statically allocating all | memory you can avoid entire classes of program faults and bugs. | There are no memory leaks and you don't need to solve the NP- | complete problem of "is there an execution path where a dynamic | memory allocation will fail". Keep in mind that it is not just | about the total amount of dynamically allocated memory, but | also the order of allocations (and frees). | tjoff wrote: | I wouldn't say this is the reason. Embedded systems typically | don't have virtual memory to start. | | I would expect (but verify) a malloc implementation on embedded | to return null if it can't satisfy the allocation. | | But even with that assumption malloc in embedded is often a bad | idea. You need to plan for worst case anyway and you can not | afford memory fragmentation. | haxorrr666 wrote: | Nope. Works fine on Fedora 34: | | # free -h total used | free shared buff/cache available | | Mem: 31Gi 3.1Gi 2.2Gi 27Mi 25Gi 27Gi | | Swap: 15Gi 62Mi 15Gi | | # uname -a | | Linux athena 5.13.19-200.fc34.x86_64 #1 SMP Sat Sep 18 16:32:24 | UTC 2021 x86_64 x86_64 x86_64 GNU/Linux | | # gcc -o memaloc memaloc.c | | # ./memaloc | | error! | yjftsjthsd-h wrote: | TLDR: "You don't." (Because malloc hands you virtual memory and | actually trying to use it might reveal that the system doesn't | have the _real_ memory to handle your request. | | I kept reading hoping that there was going to be a solution, but | not really; there are comments discussing disabling overcommit, | but even that's a tradeoff (it _does_ fix this failure mode, but | you might not want to actually run a system like that). | one_off_comment wrote: | There's gotta be some way to programmatically determine it, | right? It may not be portable. It may require some system calls | or something, but there's gotta be a way, right? | wmf wrote: | I guess you could mlock() your memory and see if that | succeeds or fails. When overcommit is enabled no memory is | really safe. | yjftsjthsd-h wrote: | I'm not an expert enough to tell you; I just read the article | and decided that it was too long so summarized for others. | There's some discussion upthread about catching SIGSEGV and | other methods, FWIW. | itamarst wrote: | This difference between "malloc() succeeded and physical/swap | memory is actually available" also has somewhat corresponding | impact on how you measure memory usage. | | One approach is RSS, the memory in physical RAM... but what if | you're swapping? Then again, maybe you swapped out memory you | don't actually need and ignoring swap is fine. | | The other approach is "how much memory you allocated", and then | you hit fun issues mentioned in this article, like "the OS | doesn't actually _really_ allocate until you touch the page". | | (Longer version: https://pythonspeed.com/articles/measuring- | memory-python/) | _3u10 wrote: | It's likely being OOM killed on Linux. Not sure what's happening | on Mac. Try allocating a terabyte of swap and it should run. | | Alternatively use mmap & mlock to verify the allocation | succeeded, but the process can still be OOM killed at any time | for any reason. | valleyer wrote: | Similarly on macOS. There is a limit of 64 gigs in the VM | compressor (in-core and on-disk compressed "segments" | combined); when this is reached, a process that owns more than | 50% of the compressed memory can be killed. | | See no_paging_space_action() in: | | https://opensource.apple.com/source/xnu/xnu-7195.81.3/bsd/ke... | | Edit: I think the 64 gigs number is out of date -- looks like | it's now based in part on the amount of physical memory in the | machine. | _3u10 wrote: | You can tell the FreeBSD / xnu devs take their job more | seriously. A failure in the VM compressor sounds so much more | professional than being OOM killed. | pcwalton wrote: | It's important to remember the biggest reason why overcommit | exists on Linux and macOS: fork(). When a process forks, the vast | majority of the child process's memory is safely shared with the | parent process, due to copy-on-write. But a strict accounting | would say that the total memory usage of the system has doubled, | which is too conservative in most cases. Since forking is so | common on Unix, overcommit ends up being necessary pretty | quickly. Otherwise, fork/exec would stop working in any process | using more than half of the system memory. | jiveturkey wrote: | Most processes that fork know that they are going to fork. | Therefore they can pre-fork very early, so as to commit the | memory when they only have a bare minimum allocated. Your | typical daemon does this anyway. | | Some other type of process like an interpreter that can | subshell out doesn't know how big the allocation is going to | get, would have to pre-fork early on. | | In this way, you wouldn't "need" overcommit and the Linux | horror of OOM. Well, perhaps you don't need it so badly. | Programs that use sparse arrays without mmap() probably need | overcommit or lots of swap. | convolvatron wrote: | wouldn't you just account the COW pages against the parent | until they are copied? | | kicking the can down the road means there isn't any longer a | reasonable correction (failing the allocation), but instead we | get to drive around randomly trying to find something to kill. | | this is particularly annoying if you are running a service. | there is no hope for it to recover - for example by flushing a | cache. instead the OS looks around - sees this fat process just | sitting there, and .. good news, we have plenty of memory now. | pjc50 wrote: | But what happens when the kernel needs to copy one and has | run out of memory? You still get a random process killed. | | (I note that Windows has a different approach, with "reserve" | vs "commit", but nobody regards that as a preferential reason | for using Windows as a server OS) | trhway wrote: | >there is no hope for it to recover - for example by flushing | a cache. instead the OS looks around - sees this fat process | just sitting there, and .. good news, we have plenty of | memory now. | | Overcommit was godsent in the times of expensive memory and | when people used virtual memory on disk (so it will spill low | use memory pages there instead of the kill). Of course these | days with abundance of cheap memory and people not | configuring virtual memory any more we get the situation you | describe. | secondcoming wrote: | Indeed. It's totally dumb that the OS is allowed lie to you | when you've asked for some resources. And because of this | behaviour people no longer check for success from malloc(), | etc, because of laziness. It's a bad situation. | joosters wrote: | Please don't blindly declare it 'totally dumb'. If you | disallow overcommit, you can end up with a system that | can't fork and run /bin/true, even if there are gigabytes | of memory left. | | Both styles of memory allocation have their uses, and their | drawbacks, but please understand them before declaring many | OS designers as stupid and dumb. | giomasce wrote: | I suppose vfork might help with that (though I don't | understand why they don't directly add fork_and_execve, | it would seem much easier). | | Also, the problem with Linux is not having overcommit, | but notv being able to choose when to overcommit and when | not. Windows makes that easier, AFAIU. | gpderetta wrote: | They do. Man posix_spawn. It comes with its own dsl to be | able to support a small subset of all operations that are | often performed between fork and execv. | | Vfork is usually a better solution. | | Edit: and yes, I would love to be able to disable | overcommit per process. | OldHand2018 wrote: | This paper [1] argues that fork needs to be deprecated in | favor of posix_spawn() or other more modern solutions. It | claims that, among many other reasons, that fork | encourages overcommit and that programs such as Redis are | extraordinarily constrained if overcommit is disabled - | because of fork. | | [1] https://dl.acm.org/doi/10.1145/3317550.3321435 | joosters wrote: | It's a weakness of the fork()+exec() model, for sure. | However, creating a fork_and_execve() API is extremely | tricky. Just think of all the innumerable setup options | you would need to give it, e.g. what file handles should | be closed or left open? What directory should it start | in? What environment variables should be set - or | cleared? And on and on and on... | | the flexibility of a separate fork() then exec() means | you can set up the initial state of a new process exactly | as you want, by doing whatever work is needed between the | two calls. If you merge them into one, then you will | never be able to encapsulate all of that. | OldHand2018 wrote: | Let's say you malloc some memory and the computer actually | has everything available. | | Everything is great, up until some other process on your | system does a fork bomb of an infinitely recursive program | that allocates nothing on the heap. You've just got a whole | lot of quickly growing stacks hoovering up your physical | memory pages. | alexgartrell wrote: | Allocated-but-unavailable is a totally reasonable part of | the memory hierarchy. | | Main Memory => zswap (compressed memory) => swap | | In this case, the pages may be logically allocated or not | -- the assurance is that the data will be the value you | expect it to be when it becomes resident. | | Should those pages be uninitialized, the "Swapped" state is | really just "Remember that this thing was all zeros." | | We could do computing your way, but it'd be phenomenally | more expensive. I know this because every thing we | introduce to the hierarchy in practice makes computing | phenomenally _less_ expensive. | cturner wrote: | "We could do computing your way, but it'd be phenomenally | more expensive" | | It must be viable - Windows prevents overcommit. But it | has slow child-process-creation (edit: previously said | "forking"), and this steers development towards native | threads which is its own set of problems. | | I had never previously joined the dots on the point | pcwalton makes at the top of this thread. It is a | dramatic trade-off. | pjc50 wrote: | Windows does not have fork(), apart from the hidden | ZwCreateProcess; | https://news.ycombinator.com/item?id=9653975 | | For ages cygwin had to emulate it manually by hand- | copying the process. | pcwalton wrote: | > wouldn't you just account the COW pages against the parent | until they are copied? | | That's what Linux does. But how do you return ENOMEM when the | copy does happen and now the system is out of memory? Memory | writes don't return error codes. The best you could do is | send a signal, which is exactly what the OOM killer does. | wahern wrote: | > Otherwise, fork/exec would stop working in any process using | more than half of the system memory. | | Somehow Solaris manages just fine. | | And don't forget that swap memory exists. Ironically, using | overcommit without swap is asking for trouble on Linux. | Overcommit or no overcommit, the Linux VM and page buffer | systems are designed with the expectation of swap. | joosters wrote: | Solaris does have this problem! If you have a huge program | running, fork()img it can fail on Solaris, even if you only | want to just exec a tiny program. The key way of avoiding | this is to ensure you have lots and lots of swap space. | pjc50 wrote: | How _does_ Solaris handle this? Or Darwin? | wahern wrote: | Solaris has strict memory accounting; fork will fail if the | system can't guarantee space for all non-shared anonymous | memory. macOS has overcommit (all BSDs do to some extent, | at least for fork), but it also automatically creates swap | space so you rarely encounter issues one way or another in | practice. | | fork and malloc can also fail in Linux even with overcommit | enabled (rlimits, but also OOM killer racing with I/O page | dirtying triggering best-effort timeout), so Linux buys you | a little convenience at the cost of making it impossibly | difficult to actually guarantee behavior when it matters | most. | _3u10 wrote: | Even without overcommit swap makes the system work better as | unused pages can be written to disk and that memory used for | disk cache and I think can also help with defragmentation of | memory not sure how that process actually works. | dekhn wrote: | The biggest reason overcommit exists is because it allows the | system to operate more efficiently. The reality is most | applications touch only some of the pages they allocate, and | it's silly for the system to fail a malloc. Often times other | expensive cleanup activities can be deferred (you don't really | want to drop a handy directory entry cache just so an app can | be sure it got physically backed memory for its request, 99.99% | of the time). | | IIUC Linux was really the first OS to make overcommit so | prominent. Most systems were a lot more conservative. | joosters wrote: | In theory, a system without overcommit can run just as | efficiently, IF you have reserved huge amounts of swap space. | As long as swap is available, the OS can do all the same COW | and efficiency tricks. It's not the physically backed memory | is the limiting factor, it's RAM+swap | dekhn wrote: | no, the kernel maintains other allocated objects (dentry, | inode caches) that it can't swap out. Under memory | pressure, those get dropped before application pages. See | https://unix.stackexchange.com/questions/17936/setting- | proc-... and | https://unix.stackexchange.com/questions/111893/how-long- | do-... | | I've found the linux memory system to be far too | complicated to understand for quite some time, compared to | what's documented in, for example, The Design and | Implementation of The FreeBSD Operating System, for a more | comprehensible system. | joosters wrote: | Those objects must exist with and without overcommit, I | don't understand why they must make one less efficient | than the other. | | (I'm talking in general here - not Linux specifically) | dekhn wrote: | dentry caches exist with and without overcommit, but you | get higher cache hit rates with overcommit, because you | flush them less recently. Depending on workload, this can | matter a lot. It mattered more in the time of hard | drives. | joosters wrote: | I'm sorry, but I'm still not following... why must a | cache be (in theory) flushed more often without | overcommit? | dekhn wrote: | if overcommit is disabled, then the system drops its | internal caches (dentry cache, pagecache) to satisfy a | memory allocation. Since most applications don't touch | pages they allocate, that means the OS could have avoided | dropping the caches. Since it did drop the caches, other | parts of the system will then have to do more work to | reconstruct the caches (looking up an dentry explicitly, | or loading a page from disk instead of RAM). | | Everything I'm describing is about a busy server with | heterogenous workloads of specific types. | secondcoming wrote: | Even the linux docs for it suggest to turn it on if you're | working with sparse arrays, there's no mention of fork() | | https://www.kernel.org/doc/Documentation/vm/overcommit-accou... ___________________________________________________________________ (page generated 2021-11-05 23:00 UTC)