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