6 ms·
As other people are commenting, malloc can still return NULL, but the critical point here is that even if you handle the NULL cases, you will still miss the oth
by joosters 11y ago
As other people are commenting, malloc can still return NULL, but the critical point here is that even if you handle the NULL cases, you will still miss the other situations where your process runs out of memory. In short, if the OS over-commits RAM, it is impossible for a program to safely handle out of memory situations.
One reason why overcommit is popular is because of fork(). Imagine a process that does malloc(lots of memory), then forks and execs a tiny program (/bin/true or something like it). If the fork() call succeeds, this means the OS has guaranteed that all the memory in the child process is available to be written over. i.e. it has had to allocate 'lots of memory' x 2 in total, even if only 'lots of memory' x 1 will actually be used.
Without overcommit, fork() can fail even if the system won't ever use anywhere close to the limit of RAM+swap space.
- JoeAltmaier 11y agoI with you on the no-point-checking-for-malloc-fail issue. It has to be done in every place, or it rarely is effective. My old colleage Mike Rowe used to call it "Putting an altimeter on your car. So if you drive over a cliff, you can see how far it is to the ground."
- joosters 11y agoDefinitely. In practice, it's impossible to do correctly. Even if your own code gets every malloc() correct (and also properly checks the return code of every syscall and library call that might fail due to lack of memory), you still have to trust that every library you use is also perfectly written and handles its memory failures just as perfectly. It'll never happen.
- JoeAltmaier 11y agoThere's just one place that it makes sense: for very large buffers. If they fail, it doesn't mean the system is doomed. So it doesn't hurt to check that video-frame buffer alloc, or that file-decompression buffer. But for any of the small change (anything within orders of magnitude of the mean allocation size) its pointless.
- zAy0LfpBZLC8mAC 11y ago> In short, if the OS over-commits RAM, it is impossible for a program to safely handle out of memory situations. That's a non-sequitur. If you don't check for NULL return, you have undefined behaviour--that is: your program might end up doing just about anything. If the OOM killer kills you, all visible effects of your program will still be perfectly consistent with the semantics of your program. And you have to be able to safely deal with that scenario anyhow, as much more fundamental resources such as electricity might run out at any time as well.
- joosters 11y agoYou're splitting hairs. Imagine your program is processing a folder of emails. If the program is halted part-way through (or if you yank the power cable out) then the mail folder will be left in an inconsistent state. If the program runs in an OS that does not overcommit memory, it is possible to write code that checks every malloc() and, if it hits a memory limit, you could shut down gracefully, fixing the mail folder so that its state is correct. If the OS overcommits, then even if you checked every malloc() call, your program might die at any time because of a SIGSEGV or the OOM killer nuking it. There's no way to tidy up an incomplete run. It's nothing to do with undefined behaviour, or dereferencing NULL pointers.
- jimrandomh 11y agoI think the point is that it's easier to use techniques that keep the mail folder always in a consistent or recoverable state, such as journaling or copy-modify-move, than it is to fully handle out-of-memory conditions. Since the former technique is both easier and can handle problems that the latter can't, it's always preferable.
- zAy0LfpBZLC8mAC 11y agoIf you leave something in a corrupted state when execution is aborted at some random point, that means that your code (and possibly your on-disk data structure) is defective. It is perfectly possible to handle that case safely (using transactions, journaling, rollback, atomic replace, ...). However, if you don't check for allocation failures, you get undefined behaviour, which you indeed cannot handle safely ... other than by avoiding the undefined behaviour in the first place by checking for allocation failures. Also, if you check for allocation failures, you won't get a SIGSEGV. SIGSEGV is for invalid virtual addresses, not for lack of resources.
- justincormack 11y agoUse posix_spawn not fork and exec, then at least the OS may not allocate twice the memory, eg the BSDs will not.
- joosters 11y agoGood luck getting any existing 3rd-party code to use posix_spawn ...
- markps 11y ago> If the fork() call succeeds, this means the OS has guaranteed that all the memory in the child process is available to be written over. i.e. it has had to allocate 'lots of memory' x 2 in total, even if only 'lots of memory' x 1 will actually be used. That isn't the case anymore, at least on Linux and BSD. fork() uses a copy-on-write scheme so the OS only allocates/copies the parent memory space if the child attempts to write to it.
- btilly 11y agoYou jut repeated the parent's point while thinking you disagreed with it. The point was that after you fork() in a copy-on-write scheme, the OS now has promised that more memory is available to write on than may actually exist. If the OS avoided overallocating, it would have to right there and then reserve lots of memory (without necessarily writing to it) just to be sure that you wouldn't run out at a later date.
- markps 11y agoYou're correct. I do agree with the point, but I misinterpreted where he was going with that example.