Go Escape Analysis Explainer
Run the compiler yourself and paste what it printed. This groups the diagnostics by cause, explains each in the compiler's own terms, and reads -m -m's indented reason chains as the derivations they are — and anything it cannot classify it shows you verbatim rather than guessing.
Nothing you paste is transmitted. There is no endpoint behind this page: the Go that reads the transcript is compiled to WebAssembly and runs in this tab, so your build output — which names your packages, your files and your identifiers — never leaves the browser.
First, run the compiler
This page does not compile anything, and that is the design rather than a stage of work. Compiling Go needs a toolchain, a module cache and a network; none of them exists in a browser tab, and a page that claimed otherwise would be doing something else without saying so. You run the real compiler on your real code, and this reads what it printed.
The diagnostics go to standard error. That is the single most common way to get nothing out of -m — a plain pipe captures standard output, which is empty.
Verdicts
go build -gcflags=-m ./... 2>escape.txt
Verdicts and the reasons behind them
go build -gcflags='-m -m' ./... 2>escape.txt
Then paste the contents of escape.txt below. Both forms are understood and you do not have to say which you used — the second one is recognizable from what it contains.
The forms this page recognizes were captured from a real compile rather than written from memory. Toolchain: —.
Start from one of these
What the flag is telling you
Escape analysis is the compiler deciding, for every value, whether its lifetime ends when the function returns. If it does, the value lives in the stack frame and costs nothing to reclaim. If the compiler cannot prove that — because the address is returned, stored in something longer-lived, captured by a closure that outlives the call, or simply too large for a frame — the value is allocated on the heap instead.
The analysis is conservative, and that is not a defect. It must be correct for every possible execution, so a value that would in practice never outlive the call still goes to the heap if the compiler cannot prove it will not. “Escapes” means “could not be proven not to”, never “does”.
The two levels are different languages
-m prints verdicts: moved to heap: p, p does not escape. -m -m prints the same verdicts and, before each one, the derivation that produced it — a header, one or more flow lines, and the steps that make up each flow, all indented beneath a repeat of the same position.
Read as flat lines that second form is mostly noise, which is why this page reads it as a tree and attaches each derivation to the verdict it explains. The pairing is by position and subject rather than by adjacency, because the compiler emits several derivations and then several verdicts, and matching them in order would attach the wrong reason to the wrong line.
Three things appear only at -m -m and are invisible at -m: why a function was not inlined, how a closure captured a variable, and the inlining cost of a function that was.
What it will not do
It does not tell you what to change. Escape analysis output says where memory came from; it says nothing about how often a function runs. A heap allocation on a path taken once at startup is not a problem, and a stack allocation in a loop body executed a billion times may still be the thing to look at. This page explains what the compiler decided and leaves the judgement where it belongs.
It does not guess. A diagnostic form the grammar does not cover is listed verbatim, with a count, under its own heading. That list is part of the answer rather than a silent remainder: a parser that quietly dropped what it did not understand would look exactly like one that understood everything.
One thing that is easy to misread
can inline f says the function is a candidate — its body is within the cost budget. It does not say any particular call to it was inlined; that is the separate inlining call to f line, emitted per call site. The distinction matters because inlining runs before escape analysis, so the same expression can be reported as escaping at one call site and staying on the stack at another.
The other tools
- Go Time Layout Converter — strftime to Go, and back — Convert a C strftime format string to a Go reference layout and back, directive by directive, with every unmappable directive named rather than guessed.
- Go Method Sets — why *T implements the interface and T does not — Paste a type and an interface and get both method sets computed by go/types, with the compiler's own error and the rule it follows from.
- Go Struct Field Alignment — offsets, padding, and what reordering saves — Paste a struct and get every field's offset, the padding between them, the total size, and a reordered version with the saving — for the architecture you choose.
- Go Slice Growth — what append actually does to the capacity — Watch a slice's capacity grow append by append: the formula's answer, the allocator's rounding, the size class it lands in, and the bytes left behind.
- Go Goroutine Dump Analyzer — read a SIGQUIT or panic stack dump — Paste a goroutine dump and get it grouped by state and by stack, with wait durations and the stacks held by far more goroutines than they should be.
- GOGC and GOMEMLIMIT Calculator — which one is actually deciding your heap — Compute both documented target-heap rules, see which one binds, and find out when GOGC has stopped having any effect at all.
- Go Benchmark Comparison — is that 4% real? — Paste two go test -bench outputs and get the Mann-Whitney U comparison: median change, p-value, and an honest answer when there is not enough data to tell.
- Go Minimal Version Selection — why that version, and who asked for it — Paste a go mod graph and see minimal version selection worked out: every version required, who required it, and the path from your main module to the requirement that won.
- Go Channel Deadlock Visualizer — every interleaving, not a guess — Write a small concurrent program in a published grammar and get every interleaving explored: the shortest path to a deadlock, what each goroutine is waiting on, and the wait-for cycle when there is one.