GoLand 2026.2 Help

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.

func NewUser(name string) *User { u := User{Name: name} // u escapes to the heap return &u }

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.

func process(data []byte) { go func() { handle(data) // data may escape: the goroutine can outlive process }() }

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.

func logValue(v int) { fmt.Println(v) // v is passed as an interface and may escape }

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.

type Cache struct { items map[string]*Item } func (c *Cache) Add(key string, it *Item) { c.items[key] = it // it escapes: stored in a structure that outlives the call }

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

  1. Select View | Tool Windows | Go Optimization from the main menu.

  2. In the Go Optimization tool window, select Escape analysis.

  3. Select the scope:

    • File: analyze the selected file.

    • Run package: analyze the selected package.

  4. Select the escape analysis messages that you want to show.

  5. (Optional) specify environment variables for the Go process.

    For example, set GOFLAGS to pass additional compiler flags, such as -N to disable compiler optimizations or -l to disable function inlining. The GOARCH and GOOS variables can also affect the output, because some compiler decisions, such as inlining and allocation, are target-dependent.

  6. Click Run Escape Analysis.

    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.

Gutter message

Inspect a gutter message

  • Hover over a gutter marker.

    In the popup, review the compiler message and the escape flow.

    Gutter message popup

You can also hover over a function name to see escape analysis results for this function.

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.

  1. Select View | Tool Windows | Go Optimization from the main menu.

  2. In the Go Optimization tool window, select events that you want to catch and click Run Escape Analysis.

  3. On the results tab, expand a file, function, or message category.

  4. Click a result.

  5. 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

  1. After running the escape analysis, on the results tab, click Filter logs.

  2. Select the message types that you want to display.

Change the results view

  1. Click View.

  2. Select one of the views:

    • Tree: shows results grouped by files, functions, and message types.

      Tree view
    • 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.

      Console output

Group results

  1. Click Group by.

  2. 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 x. Because the value is still needed after the function returns, the compiler cannot keep it on the stack and allocates it on the heap instead.

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 value is passed to another function. The compiler cannot guarantee that the function does not keep the pointer after process returns. To ensure that the value remains valid for as long as it may be needed, the compiler allocates it on the heap instead of the stack.

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 p. Because the returned pointer may still be used after the function returns, the parameter escapes the current function scope.

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 add() with the function body directly inside main(). This removes the function call overhead and allows the compiler to perform additional optimizations.

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.

13 July 2026