From 5b616633629fb08944c082a537d1fa27a2dba611 Mon Sep 17 00:00:00 2001 From: Moses Narrow <36607567+0pcom@users.noreply.github.com> Date: Tue, 1 Sep 2026 16:25:50 -0500 Subject: [PATCH] runtime: reserve 16GB of heap address space on 64-bit unix The heap reserved a flat 1GB of virtual address space, so any allocation approaching 1GB failed regardless of how much memory the system had. One example is a scrypt key derivation buffer with N=1<<20 and r=8. The reservation is virtual, so pages cost physical RAM only when first touched, and allocateHeap still halves its request if mmap refuses. 32-bit targets keep the 1GB reserve. --- src/runtime/runtime_unix.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/runtime/runtime_unix.go b/src/runtime/runtime_unix.go index 14b544b991..35e3147ff3 100644 --- a/src/runtime/runtime_unix.go +++ b/src/runtime/runtime_unix.go @@ -318,13 +318,20 @@ func procUnpin() { var heapSize uintptr = 128 * 1024 // small amount to start var heapMaxSize uintptr +// heapMaxReserve is the virtual address space reserved for the heap, 16GB on +// 64-bit targets and 1GB on 32-bit ones. Pages cost RAM only when touched. +// +// A flat 1GB cap made any allocation approaching 1GB fail regardless of +// system memory. The shift comes from TargetBits so 32-bit targets compile. +const heapMaxReserve = 1 << (30 + 4*(TargetBits/64)) + var heapStart, heapEnd uintptr func allocateHeap() { // Allocate a large chunk of virtual memory. Because it is virtual, it won't // really be allocated in RAM. Memory will only be allocated when it is // first touched. - heapMaxSize = 1 * 1024 * 1024 * 1024 // 1GB for the entire heap + heapMaxSize = heapMaxReserve for { addr := mmap(nil, heapMaxSize, flag_PROT_READ|flag_PROT_WRITE, flag_MAP_PRIVATE|flag_MAP_ANONYMOUS, -1, 0) if addr == unsafe.Pointer(^uintptr(0)) {