【Go言語学習|初心者向け】Goバイナリの肥大化を解決!go tool nmでメモリ消費の犯人を特定する方法

導入

Go言語はシングルバイナリで動作するため配布が非常に楽ですが、開発が進むにつれて「なぜかバイナリサイズがどんどん大きくなっている気がする…」と悩んだことはありませんか?バイナリサイズが肥大化すると、デプロイ時間の増加や、特にAWS Lambdaなどの実行環境でのロード時間に悪影響を及ぼします。そんな時に役立つのが go tool nm コマンドです。これを使うことで、バイナリ内のどの関数や変数がメモリ(容量)を占有しているのかを可視化し、最適化の糸口を見つけることができます。

基礎知識

go tool nm とは、Goのバイナリファイルから「シンボルテーブル」を読み取るための標準ツールです。シンボルとは、プログラム内の関数名や変数名のことです。通常、このコマンドは単にリストを表示するだけですが、オプションを組み合わせることで、各シンボルがバイナリ内でどの程度のサイズを占めているかを簡単に調査できます。

実装/解決策

バイナリサイズを調査する手順は非常にシンプルです。まずは、自身のプロジェクトをビルドして実行ファイルを作成し、そのファイルに対してコマンドを実行します。

1. プロジェクトのビルド: go build -o myapp
2. シンボルサイズ順に表示: go tool nm -size myapp

このコマンドを実行すると、左から「アドレス」「型」「サイズ」「シンボル名」の順で出力されます。ここで重要なのが「サイズ」の列です。この値が大きいものを上から確認することで、何がバイナリ容量を食っているのかを一目で特定できます。

サンプルプログラム

以下のコードは、わざと巨大な静的配列を持たせた例です。このコードをビルドして、ツールを使ってサイズを確認してみましょう。

// main.go
package main

import “fmt”

// 巨大な静的配列を定義(意図的にバイナリを肥大化させる)
var HugeData = [1000000]byte{1, 2, 3}

func main() {
// HugeDataの最初の要素を表示するだけのプログラム
fmt.Println(“データの先頭:”, HugeData[0])
}

// 確認手順:
// 1. go build -o app main.go
// 2. go tool nm -size app | grep HugeData
// 上記を実行すると、HugeDataがバイナリ内で約1MBを占めていることが確認できます。

応用・注意点

現場で活用する際のポイントは以下の通りです。

1. 巨大な静的配列に注意
コード内に直接埋め込まれた巨大な定数や配列は、そのままバイナリサイズに直結します。もし大量のデータを扱う場合は、プログラム内に埋め込むのではなく、外部ファイルとして読み込むか、S3などの外部ストレージから取得する設計を検討しましょう。

2. 肥大化した関数のリファクタリング
もし特定の関数が異常に大きい場合は、その関数が複雑すぎる(責務が多すぎる)可能性があります。関数を小さく分割することで、コンパイラの最適化が効きやすくなる場合もあります。

3. 不要な依存パッケージ
go tool nm で特定のライブラリ由来のシンボルが巨大な場合、そのライブラリが本当に必要か再考してください。`go mod graph` と組み合わせることで、依存関係の整理にもつながります。

まずは開発中の小さなツールで go tool nm -size を試して、自分の書いたコードがどのようにバイナリを構成しているのか覗いてみてください。これだけでパフォーマンスチューニングの視点がガラリと変わるはずです。

コメント

タイトルとURLをコピーしました