5 ms·
> I suppose it technically improves stability, but the cause seems like a flaw in the Windows operating system, if I'm understanding correctly. It's not a flaw
by dblohm7 4y ago
> I suppose it technically improves stability, but the cause seems like a flaw in the Windows operating system, if I'm understanding correctly.
It's not a flaw at all, when you understand what is going on. Part of the issue is that in 2022 so many developers come from Linux backgrounds that they assume that the Linux way of doing things is the "normal" or "correct" way.
The NT kernel does not overcommit, and thus does not have an OOM killer. If the kernel cannot commit pages, the system call fails. That's it. No process terminations.
Firefox would crash because (even on Windows) it uses a customized version of jemalloc that is configured to be infallible by default. If jemalloc requests pages and those pages cannot be committed, the heap allocation will fail, and thus Firefox will self-terminate. That's simply a policy decision on Firefox's part.
Going back to Windows: suppose that the commit request failed because the swap file was full. Assuming that Windows was configured to automatically resize the swap file, the OS will then grow the swap file. That's why pausing for a bit and then retrying the VM allocation works: the swap file was grown, and now the pages can be committed.
- ziml77 4y agoThe Linux design to allow for overcommit is weird to me. Why would you not want to be up-front about the fact that you don't have enough memory available to hand over to a process? The process requested a certain amount of memory to be allocated so surely it expects to use it, no?
- layer8 4y agoSee https://unix.stackexchange.com/a/521737 https://unix.stackexchange.com/a/521737 for example.
- dblohm7 4y agoThat Stack Exchange answer is kind of weird in the sense that it conflates different things. You can have demand paging without overcommitting (as NT does); the kernel simply needs to assure that there is somewhere to commit a particular page, even if that page isn't committed yet.
- layer8 4y agoThe answer may not be the best-argued one, but it links to other useful discussions, and it is correct that the forking mechanism is an important factor. Committing is limited by RAM plus swap. You’d have to reserve much more swap than is typically ever actually used by processes, at any given time.
- wizeman 4y ago> You’d have to reserve much more swap than is typically ever actually used by processes, at any given time. But if the swap wouldn't actually be typically used, then what's the problem with that? Especially considering that the amount of disk space required for that is cheap. And why not let the user decide for himself if he prefers to guarantee that their applications never get killed by the OS at the cost of reserving some disk space that he probably wouldn't ever use anyway (as most filesystems' performance nosedives after >90% disk usage anyway).
- mrob 4y ago>most filesystems' performance nosedives after >90% disk usage anyway With modern filesystems using delayed allocation to reduce fragmentation, and SSDs reducing the cost of fragmentation, you can often get good performance at higher occupancy nowadays.
- wizeman 4y agoSure, I know, but if you're talking about really modern filesystems (which do copy-on-write/CoW), then the fragmentation caused by CoW is even worse than that avoided by delayed allocation. SSDs certainly alleviate this problem, but even in SSDs, sequential I/O can be much faster than random I/O. Anyway, I guess my point is that the vast majority of systems don't run with >90% disk space usage, so reserving up to 10% of the filesystem for swap space is not unreasonable. Note that this would just be a space reservation. You wouldn't need to actually allocate specific disk blocks or write anything to the swap file, unless the system starts running out of memory. In reality, you'd need much less than 10% (in the vast majority of cases), especially if you have a Windows-like API where you can allocate address space separately from committing memory (which means uncommitted address space doesn't need swap space reservation).
- wizeman 4y ago> See https://unix.stackexchange.com/a/521737 https://unix.stackexchange.com/a/521737 for example. I'm not convinced that the fork()/exec() issue is the correct justification, at least not entirely. As an example, Solaris supports fork()/exec() yet it doesn't do overcommit (at least not by default, I think) and has no OOM killer. And Solaris has a long history of being a very robust OS. Also, in most cases I don't see what is the problem with reserving more disk space for swap (disk space which would almost never be used anyway), especially if the system becomes more robust as a result of that. I think I recall Linus justifying memory overcommit by observing that the vast majority of applications don't handle out-of-memory conditions gracefully, so it's better for the OS to kill a misbehaving process (that is allocating too much memory) and let the others continue working normally than have every single process in the system have to handle out-of-memory failures, which they don't usually handle gracefully. But I'm not sure if I'm recalling this correctly. I don't think either the overcommit design or the naive "all allocated address space must be reserved" design is the correct approach. I know almost nothing about Windows memory management, but it sounds to me like having "commit/uncommit memory" system calls separate from the "allocate/deallocate address space" system calls to be a better approach. But I think the OS should try to reserve more space for the swap file before it starts returning ENOMEM to the "commit memory" system calls like Windows seems to be doing (judging from the blog post)...
- dblohm7 4y ago> But I think the OS should try to reserve more space for the swap file before it starts returning ENOMEM to the "commit memory" system calls like Windows seems to be doing (judging from the blog post)... One could just as easily argue similarly with respect to Unix and needing to loop over write watching for EINTR et al.
- wizeman 4y ago> One could just as easily argue similarly with respect to Unix and needing to loop over write watching for EINTR et al. I'm sorry, but I fail to see how that is related. EINTR is useful so that you can handle a signal in case it is received in the middle of a very long system call. For example, if an application is doing a write() on an NFS filesystem and the NFS server is not reachable (due to some network outage), the write() syscall could take minutes or hours before it completes. So it's good, for example, than you can Ctrl-C the process or send it a SIGTERM signal and abort the syscall in the middle of it, letting the application handle the signal gracefully. What I'm talking about is not related because allocating disk space on local disk (where the swap file would be located) is generally a very quick process. Mind you, only the disk space allocation is needed for reserving swap space -- it's not necessary to write anything to the swap file. In fact, it's not even necessary to actually allocate disk space, but I digress. And even if reserving swap space would take a long time, allowing the syscall to fail with EINTR would also work fine. What is not fine is letting the application believe that the system has completely run out of memory when in fact a lot of disk space can still be used for swap reservation.
- wizeman 4y ago> Why would you not want to be up-front about the fact that you don't have enough memory available to hand over to a process? Just because a process allocates a certain amount of address space doesn't mean it will use all of it. > The process requested a certain amount of memory to be allocated so surely it expects to use it, no? No, not really. I think you are confusing memory usage with address space allocation. You may wish to allocate a huge amount of address space for convenience purposes, as it could allow you to use much simpler data structures (such as sparse arrays) which can be a lot more performant than more complicated data structures and use a lot less memory (by orders of magnitude) than the address space that you allocated for them. An example of this is AddressSanitizer's shadow memory, which is sort of a giant sparse bitmap whose address space is allocated up front, but only a small part of it is actually used at any one time. From the blog post, it sounds like Windows has a separate system call that is required to be used in-between allocating address space and actually using it. I think this is a good design and I personally prefer it over Linux's overcommit design. However, I think it has a disadvantage in that it could require you to do a lot more system calls (and therefore have slower performance) than would otherwise be needed in Linux, presumably (especially if the access patterns are unpredictable in advance?). Also, if some process misbehaves and starts using more memory than there actually exists in the system, it can cause significant problems in other processes/applications that have nothing to do with it (as their allocations would start failing). Linux handles this by killing the misbehaving process, which allows the system to recover transparently / without intervention (at the risk of possibly killing innocent processes before actually killing the misbehaving one), hopefully allowing other processes to continue working as if nothing happened. I think a reasonable approach could be to use Windows' system by default and guarantee that processes are never killed by the OS (which would benefit well-designed applications), but allow an application to optionally use overcommit if it wants the added performance (at the risk of being killed by the OOM killer if the system runs out of memory). Unfortunately, I suspect that applications which actually handle out-of-memory conditions gracefully are very rare and that most of them would just crash or kill themselves if this happens, which on average is probably a worse outcome than letting the OS kill the misbehaving application.
- ziml77 4y agoI'm not convinced I like the overcommit, but it now makes more sense to me with your distinction of asking for address space versus using memory.