はじめに
Go言語の学習中にメモリについてつまづきました。そのことについて整理するためにこの記事を投稿します。この記事では、Go言語でのメモリの「確保」と「保存」に焦点を当てます。
メモリについて意識する場面とは
例えばjson.Unmarshal
とjson.Marshal
関数かjson.NewDecoder
とjson.NewEncoder
関数のどちらを使用するかについてです。json.Unmarshal
とjson.Marshal
は、メモリにすでに全体が読み込まれているJSONデータを処理する際に適しています。これは、JSONのサイズが小さく、一度に全てのデータをメモリに読み込むことができる場合に限られます。例えば、APIからのレスポンスや小さな設定ファイルなどが該当します。一方、json.NewDecoder
とjson.NewEncoder
は、大きなサイズのJSONデータやストリーム型のデータを扱う場合に適しています。これらの関数は、データを逐次的に処理できます。これは大きなデータファイル(数GBものJSONデータ)や、リアルタイムで更新されるデータ(ソケットからのストリーミングデータなど)を扱う際に重要です。
1. メモリ領域の確保と保存の違い
メモリの「確保」は、ある種のデータを格納するためのメモリ領域を準備することを意味します。一方、「保存」は、特定の値をメモリ上に記録することを指します。
Go言語では、変数を宣言することでメモリ領域が確保されます。例えば以下です。
var myInt int
このコードは、整数を保存するためのメモリ領域を確保し、myInt
という名前をつけます。この時点では、myInt
にはゼロ値である0
が自動的に保存されます。ですので,変数の宣言だけで初期化をしていない場合でも最終的には各型のゼロ値が自動的に保存されます。
2. メモリに確保と保存がされるときと確保と保存がされない時のパターン
変数の宣言と初期化はメモリの確保と保存の一般的なパターンです。
var i int = 10
ここでは、i
という名前のメモリ領域が確保され、値10
がその領域に保存されます。
また、Goの構造体や配列などの複合データ型も同様です。以下の例では、Person
という型の変数p
が作成され、それぞれのフィールドに値が保存されます。
type Person struct {
Name string
Age int
}
p := Person{Name: "Alice", Age: 30}
ただし、型を定義するだけではメモリは確保されません。
type WeatherResponse struct {
Main struct {
Temp float64 `json:"temp"`
} `json:"main"`
Weather []struct {
Description string `json:"description"`
} `json:"weather"`
}
このWeatherResponse
型を実際に使うまで、メモリ領域は確保されません。
3. 関数とメモリ
関数が呼び出されるとき、その引数とローカル変数はメモリ(具体的にはスタック)に保存されます。関数が終了すると、そのスタックフレームは解放され、引数とローカル変数はメモリから消去されます。
func add(a int, b int) int {
var sum = a + b
return sum
}
関数add
が呼び出されると、引数a
とb
、そしてローカル変数sum
のためのメモリ領域が確保されます。関数が終了した後、これらの領域は解放されます。
まとめ
Go言語のメモリ管理は、効率的なプログラムの設計において重要な役割を果たします。メモリの確保と保存の違いを理解し、それらがいつ発生するのかを知ることで、Go言語の動作についてより深く理解することができます。