変数の有効期間は、変数のスコープと密接に関係しています。
- グローバル変数: そのライフサイクルはプログラム全体の実行サイクルと一致します。
- ローカル変数: そのライフサイクルは動的であり、変数を作成する宣言ステートメントから始まり、変数が参照されなくなるまで続きます。
- 仮パラメータと関数の戻り値: これらはすべてローカル変数であり、関数が呼び出されたときに作成され、関数呼び出しの終了後に破棄されます。
for t := 0.0; t < cycles*2*math.Pi; t += res {
x := math.Sin(t)
y := math.Sin(t*freq + phase)
img.SetColorIndex(
size+int(x*size+0.5),
size+int(y*size+0.5),
blackIndex,
}
上記のコードでは、一時変数 t が各ループの開始時に作成され、その後、一時変数 x および y が各ループ反復で作成されます。一時変数 x と y はスタックに格納され、関数の実行が終了すると (実行で最後の}
が発生する)、それらのメモリは解放されます。
スタックとヒープの違いは次のとおりです。
- ヒープ: ヒープは、プロセスの実行中に動的に割り当てられるメモリ セグメントを格納するために使用されます。サイズは固定されておらず、動的に拡大または縮小できます。プロセスが malloc などの関数を呼び出してメモリを割り当てると、新しく割り当てられたメモリがヒープに動的に追加されます (ヒープは拡張されます)。メモリの解放などの関数を使用すると、解放されたメモリはヒープから削除されます (ヒープは縮小されます)。
- スタック (スタック): スタックはスタックとも呼ばれ、プログラムによって一時的に作成されたローカル変数、つまり関数の中括弧
{ }
で定義されたローカル変数を格納するために使用されます。
プログラムのコンパイル段階で、コンパイラは実際の状況に応じてローカル変数の記憶領域をスタックまたはヒープに割り当てるよう自動的に選択します。また、変数が var または new キーワードで宣言されているかどうかは、変数に影響を与えません。コンパイラの選択。
var global *int
func f() {
var x int
x = 1
global = &x
}
func g() {
y := new(int)
*y = 1
}
上記のコードでは、関数 f 内の変数 x はヒープ上に割り当てる必要があります。これは、変数 x は関数内で定義されていますが、関数終了後もパッケージ レベルのグローバル変数を通じて見つけることができるためです。 Go 言語の用語で言えば、このローカル変数 x は関数 f からエスケープされます。
逆に、関数 g が返されると、変数 *y は使用されなくなり、すぐに再利用できることになります。したがって、 *y は関数 g からエスケープせず、コンパイラは *y の記憶領域をスタックに割り当てるか、ヒープに割り当てるかを選択し、その後 Go の GC (ガベージ コレクション メカニズム) を使用することができます。言語はこの変数空間のメモリを再利用します。
実際の開発では、変数のエスケープ動作を意図的に実装する必要はありません。エスケープされた変数には追加のメモリ割り当てが必要であり、パフォーマンスの最適化にわずかな影響が生じる可能性があるためです。
Go 言語はメモリの割り当てと解放を完了するのに役立ちますが、高パフォーマンスのアプリケーションを開発するには変数の宣言サイクルを理解する必要があります。たとえば、ローカル変数がグローバル変数に割り当てられている場合、GC がローカル変数を再利用できなくなり、不必要なメモリ占有が発生し、プログラムのパフォーマンスに影響を及ぼします。