From 5fb8e9223ae4e2fb4c95fbfad55a1e655d7d74c7 Mon Sep 17 00:00:00 2001 From: Thomas Camlong Date: Mon, 10 Aug 2026 18:43:31 +0200 Subject: [PATCH] docs(findings): the 38 MiB of compiler zones is V8's optimiser, and not reachable by flags Closes the largest unexplained item in the peak breakdown, then closes it off as a lever. Attribution: peak_malloced_memory goes 4.5 -> 42.1 MiB the moment a board's modules compile, while current malloced_memory stays at 1.0 - memory V8 takes to compile, frees back to malloc, and musl never returns to the kernel. Confirmed by re-reading the --jitless runs, where the spike never occurs at all (4.5 MiB at every stage). --jitless is unusable because it under-renders the page, but V8 can cap the optimisation tier instead of removing codegen. --max-opt is rejected by NODE_OPTIONS and accepted on the command line, so it was wired behind HOMARR_MAX_OPT and A/B'd on one image: peak anon 276.0 -> 282.8 MiB (+2%) boot idle 64.0 -> 72.3 MiB (+13%) board loaded 186.1 -> 179.2 MiB (-4%) 4 tabs 270.8 -> 238.5 MiB (-12%) after soak 199.0 -> 214.3 MiB (+8%) CPU 28.1 -> 27.8 s (unchanged) Null result. Render integrity was verified explicitly this time - listeners 7,784 in all four runs, DOM nodes within 40 - so unlike jitless the page rendered fully and the comparison is valid. The mechanism explains it: measured under --max-opt=2, peak_malloced_memory at board load is 42.1 MiB, identical to Turbofan. Maglev's zones are just as large. Capping the tier does not reduce zone memory; only not compiling does, and that breaks rendering. So this 38 MiB is not reachable through V8 configuration. It scales with how much JavaScript V8 must compile, which returns it to the single remaining server-side lever: ship less server code. Reverted. --- PHASE2-FINDINGS.md | 51 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/PHASE2-FINDINGS.md b/PHASE2-FINDINGS.md index 8fc425a79..f8a7bccb6 100644 --- a/PHASE2-FINDINGS.md +++ b/PHASE2-FINDINGS.md @@ -958,6 +958,57 @@ never observes the mid-response client disconnect that creates an orphan in the Recorded because the initial reading of the snapshot was wrong in a way that would have shipped a fix for a leak this deployment does not have. +## The 38 MiB of compiler zones: attributed, then found to be unaddressable + +The largest single item in the peak breakdown that was never explained is now explained, and then +closed off. + +**It is V8's optimising compiler.** `peak_malloced_memory` jumps from 4.5 MiB to 42.1 MiB the +moment a board's modules are compiled, while *current* `malloced_memory` stays at 1.0 MiB — memory +V8 takes for compilation, frees back to malloc, and that musl never returns to the kernel. Proven +by re-reading the `--jitless` runs, where the spike **never happens at all**: + +| stage | normal | `--jitless` | +| --- | --- | --- | +| boot idle | 4.5 MiB | 4.5 MiB | +| **board loaded** | **42.1 MiB** | **4.5 MiB** | +| multi-tab | 42.7 MiB | 4.5 MiB | + +`--jitless` itself is unusable (it leaves the page under-rendered — see above), but V8 can cap the +*tier* instead of removing code generation: `--max-opt=3` is the default Turbofan, `2` stops at +Maglev, `1` at Sparkplug. It is rejected by `NODE_OPTIONS` but accepted on the command line, so it +was wired behind `HOMARR_MAX_OPT` and A/B'd on one image. + +**`--max-opt=2` is a null result for memory:** + +| stage | full | capped at Maglev | +| --- | --- | --- | +| peak anon | 276.0 MiB | 282.8 MiB (+2%) | +| boot idle | 64.0 MiB | 72.3 MiB (+13%) | +| board loaded | 186.1 MiB | 179.2 MiB (−4%) | +| 4 tabs | 270.8 MiB | 238.5 MiB (−12%) | +| after soak | 199.0 MiB | 214.3 MiB (+8%) | +| CPU | 28.1 s | 27.8 s (unchanged) | + +Render integrity was checked explicitly this time, because that is how `--jitless` was caught: +listeners were **7,784 in all four runs** and DOM nodes within 40 of each other, so unlike jitless +the page rendered fully and the comparison is valid. There is simply no consistent memory win — +some stages better, some worse, peak unchanged. + +The mechanism says why, and it is worth recording as a closed door. Measured directly with the +probe under `--max-opt=2`: + +``` +01-boot-idle peak_malloced= 4.5 MiB +03-post-login peak_malloced= 4.5 MiB +04-board-loaded peak_malloced= 42.1 MiB <- identical to Turbofan +``` + +**Maglev's zone allocation is just as large as Turbofan's.** Capping the tier does not reduce zone +memory; only not compiling at all does, and that breaks the page. So this 38 MiB is not reachable +through V8 configuration — it is proportional to how much JavaScript V8 has to compile, which puts +it back under the one remaining server-side lever: ship less server code. Reverted. + ## jemalloc: tested, and musl is the frugal one The largest thing that was attributed but not fixed is ~38 MiB of V8 parser/compiler zone memory