summaryrefslogtreecommitdiff
path: root/notes/260821-overcommit.md
diff options
context:
space:
mode:
Diffstat (limited to 'notes/260821-overcommit.md')
-rw-r--r--notes/260821-overcommit.md92
1 files changed, 92 insertions, 0 deletions
diff --git a/notes/260821-overcommit.md b/notes/260821-overcommit.md
new file mode 100644
index 0000000..69c566a
--- /dev/null
+++ b/notes/260821-overcommit.md
@@ -0,0 +1,92 @@
+# Virtual Memory Overcommit
+
+_2026 August_
+
+There's a distinction between reserving virtual memory and having it
+"committed" by the kernel.
+
+This isn't the same thing as it being backed by physical memory. One
+could think of the state of vmem as having three levels:
+
+1. Reserved
+
+2. Committed
+
+3. Physically backed
+
+Also, the physical backing could be RAM or SWAP, but that's not
+important for our case. What matters is that reserving is always
+"free" because it's just a reservation within the virtual address
+space of the process itself, whereas committing *may* be checked
+against the available RAM/SWAP of the whole system, and possibly
+denied even before it needs physical backing. There may also be
+explicit commit limits unrelated to RAM/SWAP availability.
+
+Committing memory despite the system potentially not having enough
+RAM/SWAP is called overcommit.
+
+## When is overcommit allowed
+
+According to some quick research (don't cite this) the breakdown of
+which kernels allow overcommit is as follows:
+
+* MacOS: Always allowed.
+
+* Linux, FreeBSD, NetBSD: Allowed by default; configurable.
+
+* OpenBSD, MS Windows: Not allowed.
+
+So, for Zisp to cover even just all POSIX systems, it will need some
+way to reserve without committing.
+
+## How to reserve without commit
+
+Under POSIX, you reserve without committing by calling `mmap()` with
+`PROT_NONE` for the protection argument. Of course, the behavior is
+not actually documented as part of POSIX, but I suppose it should be
+safe to assume that any sane implementation would not count memory
+that's inaccessible towards any kind of commit limit. Surprisingly,
+even the Linux documentation doesn't *explicitly* guarantee that a
+`PROT_NONE` mapping is "free" but I think it's safe to assume given
+there's projects in the wild like jemalloc and the JVM that already
+use this trick, so Linux wouldn't dare change the behavior.
+
+There's also `MAP_NORESERVE` under Linux, which can achieve the same
+effect, but I guess it's less portable. Funnily, there's even some
+old discussions about the kernel not honoring `MAP_NORESERVE` under
+strict overcommit policies, while `PROT_NONE` works. Oh and also,
+"NORESERVE" flies in the face of the terminology I use here, and
+there's also comments on LWN about how it's a misnomer:
+
+[https://lwn.net/Articles/627557/](https://lwn.net/Articles/627557/)
+
+Anyhow, TL;DR: Use `PROT_NONE` on POSIX; it should hopefully work as
+desired on any common POSIX system besides OpenBSD.
+
+On MS Windows, you reserve by calling `VirtualAlloc()` with the
+`MEM_RESERVE` flag, and commit with the `MEM_COMMIT` flag.
+
+## Committing as you go
+
+When you've reserved without commit, you then actually need to tell
+the kernel when you need the memory committed.
+
+On POSIX, you can't use it under `PROT_NONE` anyway (the whole point)
+and asking the system to commit it is a matter of calling `mprotect()`
+to set the protection mode to `PROT_READ | PROT_WRITE` instead.
+
+Likewise, on MS Windows, trying to access in any way a region of vmem
+not yet committed is an error; you need to call `VirtualAlloc` again,
+on a sub-range of the full range, with `MEM_COMMIT` this time.
+
+## Disadvantages
+
+Syscalls are costly! Would be great if we only ever needed a single
+one at startup, but alas.
+
+Thankfully, after my latest tweaks to Meta Alloc, it already bumps
+watermarks in chunks of at least 64 KiB. That's a perfect spot for
+inserting the `mprotect()` or `VirtualAlloc()` calls.
+
+I may just do that unconditionally, at least for now, because it's
+easy to make it configurable if ever needed.