Goのガベージコレクション
CoddyのGOジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 23/107。
CやC++のような言語とは異なり、Goはガベージコレクション(GC)を通じてメモリ管理を自動的に行います。メモリを使い終わったときに、手動で解放する必要はありません。Goのランタイムは、どのメモリがまだ使用中であるかを追跡し、残りを回収します。
new()、make()、または変数の作成によってメモリを割り当てると、Goのガベージコレクタはこれらの割り当てを監視します。プログラムのどの部分からも値に到達できなくなると、GCはそのメモリを自動的に解放します。
func createPerson() *Person {
p := &Person{Name: "Alice", Age: 30}
return p // pはヒープにエスケープされ、GCが管理します
}
func main() {
person := createPerson()
fmt.Println(person.Name)
// personが使用されなくなると、GCがメモリを回収します
}この例では、Person 構造体は関数から返されるため、ヒープ上に割り当てられます。Goのコンパイラは、変数がスタックに留まることができるか、それともヒープに移動する必要があるかを判断するために、エスケープ解析を実行します。ガベージコレクタはヒープメモリのみを管理します。
この自動メモリ管理により、解放し忘れによるメモリリークを心配することなく、アプリケーションの構築に集中できます。しかし、GCが存在することを理解しておくことは、パフォーマンスが重要な場合に不要なアロケーションを最小限に抑え、より効率的なコードを書くのに役立ちます。
チャレンジ
簡単Goのガベージコレクタが、ヒープにエスケープしたオブジェクトのメモリをどのように処理するかを実証するセッションマネージャーを構築しましょう。動的に割り当てられ、関数から返されるセッションを作成し、そのライフサイクルをGCに管理させます。
コードは2つのファイルに分けて構成します:
session.go:ID(string)、Username(string)、およびData(文字列のスライス) フィールドを持つSession構造体を定義します。IDとユーザー名を受け取り、新しい Session をヒープに割り当て(ポインタを返すことで)、Data スライスを空として初期化するNewSession関数を作成します。また、セッションの Data スライスに文字列を追加するポインタレシーバを持つAddDataメソッドと、セッションの詳細を表示するフォーマット済み文字列を返すSummaryメソッドを追加します。main.go: 入力からセッション情報を読み取り、コンストラクタ関数を使用してセッションを作成し、いくつかのデータエントリを追加して、セッションのサマリーを表示します。セッションは関数内で作成され、ポインタとして返されるため、ヒープにエスケープし、ガベージコレクタによって管理されます。
以下の入力が提供されます:
- 1行目: Session ID
- 2行目: Username
- 3行目: 追加する1つ目のデータエントリ
- 4行目: 追加する2つ目のデータエントリ
Summary メソッドは、以下の形式の文字列を返す必要があります:
Session [ID] for user [Username]
Data entries: [count]
- [entry1]
- [entry2]例えば、sess-001、alice、login、view_dashboard が与えられた場合、出力は以下のようになります:
Session sess-001 for user alice
Data entries: 2
- login
- view_dashboardここでの重要な洞察は、NewSession 関数が Session を作成し、そのポインタを返すということです。これにより、Session はスタックに留まるのではなくヒープに「エスケープ」し、参照がなくなった時点でガベージコレクションの対象になります。このメモリを手動で解放する必要はありません。Goが自動的に処理します。
チートシート
Goは、自動メモリ管理のためにガベージコレクション(GC)を使用します。手動でメモリを解放する必要はありません。
ガベージコレクタはメモリ割り当てを追跡し、プログラムから到達できなくなったメモリを回収します。
func createPerson() *Person {
p := &Person{Name: "Alice", Age: 30}
return p // pはヒープにエスケープし、GCが管理します
}
func main() {
person := createPerson()
fmt.Println(person.Name)
// personが使用されなくなると、GCがメモリを回収します
}Goのコンパイラは、変数がスタックに残るかヒープに移動するかを決定するためにエスケープ解析を実行します。ヒープにエスケープする変数(返されるポインタなど)は、ガベージコレクタによって管理されます。
GCはヒープメモリのみを管理し、スタックメモリは管理しません。スタックメモリは、関数が戻るときに自動的に解放されます。
自分で試してみよう
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// セッションIDを読み込む
sessionID, _ := reader.ReadString('\n')
sessionID = sessionID[:len(sessionID)-1]
// ユーザー名を読み込む
username, _ := reader.ReadString('\n')
username = username[:len(username)-1]
// 最初のデータエントリを読み込む
data1, _ := reader.ReadString('\n')
data1 = data1[:len(data1)-1]
// 2番目のデータエントリを読み込む
data2, _ := reader.ReadString('\n')
data2 = data2[:len(data2)-1]
// TODO: NewSession 関数を使用して新しいセッションを作成する
// ポインタを返すため、セッションはヒープにエスケープされます
// TODO: AddData メソッドを使用してデータエントリをセッションに追加する
// TODO: Summary メソッドを使用してセッションの概要を出力する
fmt.Println("")
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。
オブジェクト指向プログラミングのすべてのレッスン
1Go オブジェクト指向の基礎
外部ファイルGo ワークスペースとモジュールパッケージとインポート公開された名前と非公開の名前Go におけるオブジェクト指向入門クラスとしての構造体構造体へのメソッド定義ポインタレシーバと値レシーバ構造体の初期化コンストラクタ関数まとめ:簡易計算機8エラー処理とOOP
error インターフェースカスタムエラー型エラーラッピング (fmt.Errorf)センチネルエラーerrors.Is() と errors.As()Panic、Defer、Recover復習 - ファイルパーサー