Escape analysis
Escape analysis helps you understand why the Go compiler places values on the heap instead of the stack. Heap allocations can increase memory usage and create more work for the garbage collector.
GoLand runs escape analysis by using the go build -gcflags="-m=2 -json=0,<path>" command, which stores the compiler diagnostics in a structured JSON format. GoLand processes the command output and applies the results to your code. This helps you navigate to compiler messages, inspect escape flows, and understand optimization decisions directly in the editor.
Why values escape to the heap
The stack is a per-goroutine region of memory where allocations are cheap and reclaimed automatically when a function returns. The heap is shared, longer-lived memory that the garbage collector has to track and clean up, so it's more expensive. A value escapes to the heap when the compiler can't prove that nothing outside the current function still needs it after the function returns.
Escape decisions can change depending on how the code is structured, the Go version, the target platform, and other optimization decisions such as inlining. A handful of patterns account for most escapes:
Returning pointers
Returning a pointer to a local value is one of the most common causes of an escape. The value is created inside the function, but the caller holds onto a reference after the function returns, so the value can't live on the stack frame that's being torn down.
This pattern is safe and idiomatic in Go. Returning pointers is often the right choice for API clarity, regardless of whether the value escapes.
Closures and goroutines
A variable captured by a closure or a goroutine escapes when the closure or goroutine may outlive the function that created it. The compiler has to assume the captured value is still reachable, so it allocates it on the heap.
Interfaces and dynamic values
Passing a concrete value through an interface can contribute to a heap allocation. This shows up most often in formatting, logging, and interface-based APIs, where a value is boxed into an interface{} (any) before being handled.
Passing a value through an interface doesn't always cause a heap allocation. Treat interface boundaries as something to check in the escape analysis output rather than avoid outright.
Slices, maps, and structs
A value escapes when it's stored inside a data structure that outlives the current function. If a pointer is stored in a map, a slice, or a struct field, and that container lives longer than the function, the stored value has to live just as long.
Not every escape is a problem. Programs of any complexity will have values that need to live on the heap, and escape analysis is most useful for investigating allocations on hot paths, not for eliminating every heap allocation.
Run escape analysis
You can run escape analysis for a file or for a package.
Run escape analysis for a file or package
Select from the main menu.
In the Go Optimization tool window, select Escape analysis.
Select the scope:
File: analyze the selected file.
Run package: analyze the selected package.
Select the escape analysis messages that you want to show.
(Optional) specify environment variables for the Go process.
For example, set
GOFLAGSto pass additional compiler flags, such as-Nto disable compiler optimizations or-lto disable function inlining. TheGOARCHandGOOSvariables can also affect the output, because some compiler decisions, such as inlining and allocation, are target-dependent.Click Run Escape Analysis.

View results in the editor
After the analysis finishes, GoLand shows escape analysis messages in the editor gutter.
If several messages belong to the same line, the gutter marker shows the number of logs for this line, for example, 2 logs.

Inspect a gutter message
Hover over a gutter marker.
In the popup, review the compiler message and the escape flow.

You can also hover over a function name to see escape analysis results for this function.
Navigate from results to code
GoLand also shows escape analysis results in the Go Optimization tool window.
Click a result in the tool window to navigate to the corresponding line in the editor.
Navigate to a result in code
Select from the main menu.
In the Go Optimization tool window, select events that you want to catch and click Run Escape Analysis.
On the results tab, expand a file, function, or message category.
Click a result.
GoLand moves the caret to the corresponding line in the editor.
Filter and group results
You can filter escape analysis messages in the editor and in the tool window.
Filter escape analysis messages
After running the escape analysis, on the results tab, click Filter logs.
Select the message types that you want to display.
Change the results view
Click View.
Select one of the views:
Tree: shows results grouped by files, functions, and message types.

Console output: shows the raw output of the
go build -gcflags="-m=2 -json=0,<path>"command. You can click the results in this view to navigate to the corresponding line.
Group results
Click Group by.
Select how GoLand groups the results, for example, by function or by category.
Compare escape analysis results
After you change your code, rerun escape analysis to check whether an allocation actually moved off the heap. Each run opens in its own tab in the Go Optimization tool window, so you can switch between the tabs to compare the results before and after the change.
Understand common escape analysis messages
Escape analysis determines whether values can be allocated on the stack or must be allocated on the heap.
Many heap allocations are required and are not performance problems. Use escape analysis together with benchmarks and profiling results to find allocations that affect performance-critical code.
Message type | Description |
|---|---|
Escape to Heap | Indicates that a value must be allocated on the heap because it is still needed after the current function returns.
func create() *int {
x := 10
return &x
}
In this example, the function returns the address of |
Moved to Heap | Indicates that the compiler allocated a value on the heap because it could not guarantee that the value would no longer be needed after the current function returns.
func process() {
value := 42
use(&value)
}
In this example, the address of |
Leak Param | Indicates that a function parameter escapes the current function and may need to remain valid after the function returns.
func store(p *int) *int {
return p
}
In this example, the function returns the parameter |
Can Inline | Indicates that the function is small and simple enough for the compiler to replace the function call with the function body during compilation.
func add(a, b int) int {
return a + b
}
In this example, the function contains a simple operation and has a small body. The compiler can inline the function to reduce function call overhead and improve optimization opportunities. |
Inlining Call | Indicates that the compiler replaced a function call with the function body during compilation.
func add(a, b int) int {
return a + b
}
func main() {
_ = add(1, 2)
}
In this example, the compiler replaces the call to Can Inline indicates that a function is eligible for inlining. Inlining Call indicates that the compiler actually inlined a specific function call. |
Other | Displays additional compiler diagnostics related to escape analysis and optimization decisions. |