GoLand 2026.3 Help

エスケープ解析​分析​

エスケープ解析は、値がスタックメモリとヒープメモリのどちらを使用するかを決定するコンパイラー最適化です。 ヒープ割り当てによってメモリ使用量が増え、ガベージコレクターの作業が増えることがあります。

GoLand は go build を -gcflags=-m=2 および -json=0,<path> とともに実行します。 コンパイラー診断を読み取り、エディターと Go の最適化 ツールウィンドウに表示します。 メッセージと、値がエスケープする原因となったフローを確認できます。

なぜ値がヒープに逃げるのか

コンパイラーは、有効期間を判別できる値にスタックメモリを使用します。 有効期間を判別できない値にはヒープメモリを使用します。 コンパイラーが、現在の関数から戻った後に値が不要になることを証明できない場合、その値はヒープにエスケープします。 ガベージコレクターはヒープメモリを管理します。

エスケープの判断は、コードの構造や Go のバージョン、ターゲットプラットフォーム、さらにインライン化などその他の最適化の判断によって変わる場合があります。 ほとんどのエスケープは少数のパターンに集約されます:

エスケープは推移的になることがあります。 参照を含む値がエスケープすると、その参照が使用されている間は有効であり続ける必要があるため、参照先の値もエスケープすることがあります。

ポインターの返却

ローカル値へのポインターを返すことは、エスケープの最も一般的な原因のひとつです。 値は関数内で作成されますが、関数が戻った後も呼び出し元が参照を保持します。 値はその関数のスタックフレームに残ることができません。

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

このパターンは Go で安全かつ慣用的です。 値がエスケープするかどうかに関わらず、API の明確さのためにはポインターを返すのが適切なことが多いです。

クロージャとゴルーチン

クロージャまたはゴルーチンでキャプチャされた変数は、それらが生成元の関数の寿命を超えて残る可能性があるときにエスケープします。 コンパイラーはキャプチャされた値がまだ到達可能だとみなさなければならないため、ヒープ上に割り当てます。

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

インターフェースと動的な値

具象値をインターフェース経由で渡すと、ヒープ割り当てにつながる場合があります。 これはフォーマット、ロギング、インターフェースベースの API でよく発生し、その場合、コンパイラーは値を interface{} (any) として渡します。

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

インターフェースを介して値を渡しても、必ずしもヒープ割り当てが発生するわけではありません。 インターフェースの境界は、避けるのではなくエスケープ解析の出力で確認すべきポイントです。

スライス、マップ、構造体

値は、現在の関数よりも長く存続するデータ構造に保存されると、エスケープすることがあります。 ポインターがマップ、スライス、または構造体フィールドに保存され、そのコンテナーが関数よりも長く存続する場合、ポインターが指す値も同じ期間有効であり続ける必要があります。

type Item struct{} var cache = map[string]*Item{} func addItem() { item := Item{} cache["item"] = &item // item escapes: cache can outlive addItem }

実行時に決定されるサイズを持つ値

コンパイラーがコンパイル時にサイズを判別できない場合、値がエスケープすることがあります。 たとえば、スライスのバッキング配列は、そのサイズが変数から得られる場合にエスケープすることがあります。

すべてのエスケープが問題になるわけではありません。 どんな複雑さを持つプログラムでも、ヒープ上で生き続けなければならない値が存在します。エスケープ解析はホットパス上の割り当て調査に最も役立ち、すべてのヒープ割り当てをなくすためのものではありません。

エスケープ解析を実行する

ファイルやパッケージごとにエスケープ解析を実行できます。

ファイルまたはパッケージのエスケープ解析を実行する

  1. メインメニューから 表示 | Tool Windows | Go 最適化 を選択します。

  2. Go の最適化 ツールウィンドウで、 エスケープ解析​分析​ を選択します。

  3. スコープを選択してください:

    • ファイル: 選択したファイルを解析します。

    • パッケージを実行: 選択したパッケージを解析します。

  4. 表示したいエスケープ解析メッセージを選択します。

  5. (オプション)Go プロセスの環境変数を指定します。

    たとえば追加のコンパイラーフラグを渡すには GOFLAGS を設定します。最適化を無効化する -N や関数インライン化を無効化する -l などがあります。 GOARCH および GOOS 変数も出力に影響することがあります。インライン化や割り当てなど一部のコンパイラー判断はターゲット依存だからです。

  6. エスケープ解析の実行 をクリックしてください。

    エスケープ解析を実行する

結果はプロジェクト解析の完了後に表示されます。 GoLand がまだプロジェクトのインデックス作成を行っている場合、エスケープ解析タブにはインデックスの準備ができるまで進捗が表示されます。

実行を停止すると、その進捗タブが閉じます。 実行が失敗した場合、GoLand はそのタブを閉じ、エラーの詳細を含む エスケープ解析に失敗しました 通知を表示します。

エディターで結果を表示する

解析が完了すると、GoLand はエディターのガターにエスケープ解析メッセージを表示します。

複数のメッセージが同じ行に属する場合、ガターマーカーはこの行のログ数を表示します。例えば、 2 件のログ。

ガターメッセージ

ガターメッセージを確認する

  • ガターマーカーにカーソルを合わせます。

    ポップアップでコンパイラーメッセージとエスケープフローを確認します。

関数名にカーソルを合わせると、その関数のエスケープ解析結果を表示できます。

GoLand は Go の最適化 ツールウィンドウにもエスケープ解析結果を表示します。

結果をダブルクリックするか、選択して Enter キーを押すと、対応する行がエディターで開きます。

各結果には、行番号と名前またはソース式が表示されます。 結果がカテゴリ別にグループ化されていない場合、カテゴリが名前の横に表示されます。 結果を選択すると、詳細ペインでコンパイラーメッセージとエスケープフローを確認できます。

  1. メインメニューから 表示 | Tool Windows | Go 最適化 を選択します。

  2. Go の最適化 ツールウィンドウで、キャッチしたいイベントを選択し、 エスケープ解析の実行 をクリックします。

  3. 結果タブでファイル、関数、またはメッセージカテゴリを展開します。

  4. 結果をダブルクリックするか、選択して Enter キーを押します。

  5. GoLand はエディターの該当行にキャレットを移動します。

結果をフィルターおよびグループ化する

エディターおよびツールウィンドウでエスケープ解析メッセージをフィルターできます。

エスケープ解析メッセージをフィルターする

  1. エスケープ解析を実行した後 、結果タブで ログをフィルタリングする をクリックします。

  2. 表示したいメッセージタイプを選択します。

結果ビューを変更する

  1. View をクリックしてください。

  2. ビューのいずれかを選択してください:

    • ツリー: 解析済み結果のデフォルトビュー。 結果をファイル、関数、メッセージタイプ別にグループ化します。

    • コンソール出力: 解析実行からの未加工のコンパイラー出力を表示します。 このビューの結果から、対応する行に移動できます。

      コンソール出力

結果をグループ化する

  1. グループ化 をクリックしてください。

  2. GoLand で結果をどのようにグループ化するかを選択してください(例:関数単位やカテゴリ単位)。

コンパイラー生成エントリは、 <autogenerated> などの名前の下にグループ化されます。 これらのグループは、ソースファイルを含むディレクトリの後に表示されます。

エスケープ解析結果を比較

コードを変更したら、エスケープ解析を再実行して割り当てが実際にヒープから移動したかどうか確認してください。 各実行は Go の最適化 ツールウィンドウの個別のタブで開かれるため、変更前後の結果をタブ間で比較できます。

一般的なエスケープ解析メッセージを理解する

エスケープ解析は、値がスタックに割り当てられるか、ヒープに割り当てられる必要があるかを判定します。

多くのヒープ割り当ては必要であり、パフォーマンス上の問題ではありません。 エスケープ解析はベンチマークやプロファイリング結果と組み合わせて使い、パフォーマンスに影響する割り当てを特定してください。

メッセージタイプ

説明

ヒープへのエスケープ

値が現在の関数の戻り値で必要となるためヒープに割り当てるべきであることを示します。

func create() *int { x := 10 return &x }

この例では、関数が x のアドレスを返します。 関数の戻り後も値が必要となるため、コンパイラーはその値をスタックではなくヒープに割り当てます。

ヒープへ移動済み

関数の戻り後に値が不要になると保証できないため、コンパイラーが値をヒープに割り当てたことを示します。

func process() { value := 42 use(&value) }

この例では、 値 のアドレスが別の関数へ渡されます。 コンパイラーは、 process の戻り後に関数がポインターを保持していないことを保証できません。 値が必要な限り有効であることを保証するため、コンパイラーはその値をスタックではなくヒープに割り当てます。

パラメーターのリーク

関数パラメーターが関数から逃げ、関数の戻り後も有効である必要があることを示します。

func store(p *int) *int { return p }

この例では、関数がパラメーター p を返します。 返されたポインターが関数の戻り後も使用される可能性があるため、パラメーターは現在の関数スコープから逃げます。

インライン化可能

関数が十分に小さく単純なため、コンパイラーが関数呼び出しを関数本体で置き換えられることを示します。

func add(a, b int) int { return a + b }

この例では、関数はシンプルな処理を含み、小さな本体を持っています。 コンパイラーは関数呼び出しのオーバーヘッドを減らし、最適化の機会を増やすために関数をインライン化できます。

呼び出しのインライン化

コンパイラーが関数呼び出しをコンパイル時に関数本体で置換したことを示します。

func add(a, b int) int { return a + b } func main() { _ = add(1, 2) }

この例では、コンパイラーが add() の呼び出しを main() の内部で直接関数本体に置き換えます。 これにより関数呼び出しのオーバーヘッドが削減され、コンパイラーによるさらなる最適化が可能になります。

インライン化可能 は関数がインライン化の対象であることを示します。 呼び出しのインライン化 はコンパイラーが特定の関数呼び出しを実際にインライン化したことを示します。

その他

エスケープ解析や最適化の判断に関連する追加のコンパイラー診断を表示します。

2026 年 11 月 6 日