[解決済み】Goの構造体のスタックとヒープ割り当て、およびガベージコレクションとの関連性
質問
私はGoの初心者ですが、自動変数がスタック上にあり、割り当てられたメモリがヒープ上にあるCスタイルのスタックベースのプログラミングと、スタック上にあるのはヒープ上のオブジェクトへの参照/ポインタだけであるPythonスタイルのスタックベースのプログラミングの間で、ちょっとした認知的不協和を経験しています。
私が見る限り、次の2つの関数は同じ出力をします。
func myFunction() (*MyStructType, error) {
var chunk *MyStructType = new(HeaderChunk)
...
return chunk, nil
}
func myFunction() (*MyStructType, error) {
var chunk MyStructType
...
return &chunk, nil
}
つまり、新しい構造体を割り当てて、それを返します。
もしC言語でこれを書いたとしたら、1つ目はヒープに、2つ目はスタックにオブジェクトを置くことになる。1番目はヒープへのポインタを返し、2番目はスタックへのポインタを返しますが、関数が戻るまでにスタックは蒸発してしまうでしょう。
もし、Python(あるいはC#以外の多くの現代言語)で書いていたら、例2は不可能だったでしょう。
Goが両方の値をガベージコレクションするのはわかったので、上記のどちらの形式も問題ないです。
引用すると
<ブロッククオートC言語と異なり、あるアドレスの値を返すことは全く問題ありません。 ローカル変数は、その変数に関連するストレージが存続します。 関数が戻った後 実際、複合的な リテラルは評価されるたびに新しいインスタンスを割り当てます。 この最後の2行を組み合わせることができます。
http://golang.org/doc/effective_go.html#functions
しかし、いくつかの疑問が生じます。
-
例1では、構造体はヒープ上で宣言されています。例2はどうでしょう?C言語と同じようにスタック上で宣言されているのでしょうか、それともヒープ上でも宣言されているのでしょうか?
-
例2がスタック上で宣言されている場合、関数が戻った後、どのようにして利用可能な状態を維持するのですか?
-
例2が実際にヒープ上で宣言されている場合、構造体が参照ではなく値で渡されるのはなぜでしょうか?この場合、ポインターは何のためにあるのでしょうか?
どのように解決するのですか?
注目すべきは、言語仕様のどこにも "stack" と "heap" という単語が登場しないことです。 ご質問は "...is declared on the stack," と "...declared on the heap," で表現されていますが、Go の宣言構文にはスタックやヒープについて何も書かれていないことに注意してください。
そのため、技術的にはすべての質問の回答は実装依存になります。 もちろん実際には、スタック(ゴルーチンごと!)とヒープがあり、あるものはスタックに、あるものはヒープに置くことになります。 場合によっては、コンパイラが厳格なルール(例えば "
new
また、あるオブジェクトがスタックに置けるか、ヒープに置かなければならないかを判断するために、コンパイラは「エスケープ分析」を行うこともあります。
例2の場合、エスケープ分析により構造体へのポインタがエスケープされるため、コンパイラは構造体を割り当てなければなりません。 しかし、現在のGoの実装では、この場合、構造体のいずれかの部分のアドレスが取得されると、その構造体はヒープに置かれるという厳格なルールに従うと思います。
質問3は、用語が混乱する恐れがあります。 Goではすべて値で渡され、参照渡しはありません。 ここでは、ポインタの値を返しています。 ポインターは何のためにあるのでしょうか? あなたの例を次のように変更して考えてみましょう。
type MyStructType struct{}
func myFunction1() (*MyStructType, error) {
var chunk *MyStructType = new(MyStructType)
// ...
return chunk, nil
}
func myFunction2() (MyStructType, error) {
var chunk MyStructType
// ...
return chunk, nil
}
type bigStruct struct {
lots [1e6]float64
}
func myFunction3() (bigStruct, error) {
var chunk bigStruct
// ...
return chunk, nil
}
myFunction2 は、構造体のアドレスではなく、構造体を返すように修正しました。 myFunction1 と myFunction2 のアセンブリ出力を比較してみてください。
--- prog list "myFunction1" ---
0000 (s.go:5) TEXT myFunction1+0(SB),$16-24
0001 (s.go:6) MOVQ $type."".MyStructType+0(SB),(SP)
0002 (s.go:6) CALL ,runtime.new+0(SB)
0003 (s.go:6) MOVQ 8(SP),AX
0004 (s.go:8) MOVQ AX,.noname+0(FP)
0005 (s.go:8) MOVQ $0,.noname+8(FP)
0006 (s.go:8) MOVQ $0,.noname+16(FP)
0007 (s.go:8) RET ,
--- prog list "myFunction2" ---
0008 (s.go:11) TEXT myFunction2+0(SB),$0-16
0009 (s.go:12) LEAQ chunk+0(SP),DI
0010 (s.go:12) MOVQ $0,AX
0011 (s.go:14) LEAQ .noname+0(FP),BX
0012 (s.go:14) LEAQ chunk+0(SP),BX
0013 (s.go:14) MOVQ $0,.noname+0(FP)
0014 (s.go:14) MOVQ $0,.noname+8(FP)
0015 (s.go:14) RET ,
ここでのmyFunction1の出力は、peterSOの(素晴らしい)答えと違うので心配しないでください。 私たちは明らかに異なるコンパイラを使用しています。 その他に、myFunction2が*myStructTypeではなくmyStructTypeを返すように修正したことを見てください。 runtime.newの呼び出しがなくなったのは、場合によっては良いことかもしれませんね。 しかし、ここでmyFunction3が登場する。
--- prog list "myFunction3" ---
0016 (s.go:21) TEXT myFunction3+0(SB),$8000000-8000016
0017 (s.go:22) LEAQ chunk+-8000000(SP),DI
0018 (s.go:22) MOVQ $0,AX
0019 (s.go:22) MOVQ $1000000,CX
0020 (s.go:22) REP ,
0021 (s.go:22) STOSQ ,
0022 (s.go:24) LEAQ chunk+-8000000(SP),SI
0023 (s.go:24) LEAQ .noname+0(FP),DI
0024 (s.go:24) MOVQ $1000000,CX
0025 (s.go:24) REP ,
0026 (s.go:24) MOVSQ ,
0027 (s.go:24) MOVQ $0,.noname+8000000(FP)
0028 (s.go:24) MOVQ $0,.noname+8000008(FP)
0029 (s.go:24) RET ,
まだruntime.newは呼び出されていませんし、8MBのオブジェクトを値で返すことは本当にうまくいっています。 動作はするが、通常はそうしたくないだろう。 ここでポインターを使う意味は、8MBのオブジェクトを押しまくるのを避けるためでしょう。
関連
-
[解決済み] Go モジュールのローカル リポジトリとして Artifactory を使用する方法
-
[解決済み] GORMでレコードを作成・更新するには?
-
[解決済み] 環境変数GOPRIVATEの設定方法
-
[解決済み] Goテスト文字列が部分文字列を含む
-
[解決済み] 全モジュールのアップデートを行う
-
[解決済み] Goで2つのスライスを連結する
-
[解決済み] どちらが速いですか?スタックアロケーションとヒープアロケーション
-
[解決済み】init()関数はいつ実行されるのですか?
-
[解決済み】アプリケーションの自動ビルドによるバージョン管理
-
[解決済み】スライスの最後の要素を取得する方法は?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] テスト
-
[解決済み] インポート "google/api/annotations.proto" が見つからないか、エラーが発生しました。依存関係として追加するにはどうすればよいですか?
-
[解決済み] キューの実装はありますか?
-
[解決済み] go get でパッケージをインストールするにはどうしたらいいですか?
-
[解決済み] Goのペア/タプルデータ型
-
[解決済み] goインストールとは何ですか?
-
[解決済み] golangでnilはどういう意味ですか?
-
[解決済み] Goで部分文字列を抽出する
-
[解決済み] 2枚のスライスの等価性を確認する
-
[解決済み】Goの構造体にデフォルト値を設定する方法