零零乙 2012-06-02 21:46 采纳率: 33.3%
浏览 268
已采纳

堆栈与堆分配的结构在去,以及它们如何涉及到垃圾收集

I'm new to Go and I'm experiencing a bit of congitive dissonance between C-style stack-based programming where automatic variables live on the stack and allocated memory lives on the heap and and Python-style stack-based-programming where the only thing that lives on the stack are references/pointers to objects on the heap.

As far as I can tell, the two following functions give the same output:

func myFunction() (*MyStructType, error) {
    var chunk *MyStructType = new(HeaderChunk)

    ...

    return chunk, nil
}


func myFunction() (*MyStructType, error) {
    var chunk MyStructType

    ...

    return &chunk, nil
}

i.e. allocate a new struct and return it.

If I'd written that in C, the first one would have put an object on the heap and the second would have put it on the stack. The first would return a pointer to the heap, the second would return a pointer to the stack, which would have evaporated by the time the function had returned, which would be a Bad Thing.

If I'd written it in Python (or many other modern languages except C#) example 2 would not have been possible.

I get that Go garbage collects both values, so both of the above forms are fine.

To quote:

Note that, unlike in C, it's perfectly OK to return the address of a local variable; the storage associated with the variable survives after the function returns. In fact, taking the address of a composite literal allocates a fresh instance each time it is evaluated, so we can combine these last two lines.

http://golang.org/doc/effective_go.html#functions

But it raises a couple of questions.

1 - In example 1, the struct is declared on the heap. What about example 2? Is that declared on the stack in the same way it would be in C or does it go on the heap too?

2 - If example 2 is declared on the stack, how does it stay available after the function returns?

3 - If example 2 is actually declared on the heap, how is it that structs are passed by value rather than by reference? What's the point of pointers in this case?

转载于:https://stackoverflow.com/questions/10866195/stack-vs-heap-allocation-of-structs-in-go-and-how-they-relate-to-garbage-collec

  • 写回答

4条回答 默认 最新

  • 衫裤跑路 2012-06-03 00:01
    关注

    It's worth noting that the words "stack" and "heap" do not appear anywhere in the language spec. Your question is worded with "...is declared on the stack," and "...declared on the heap," but note that Go declaration syntax says nothing about stack or heap.

    That technically makes the answer to all of your questions implementation dependent. In actuality of course, there is a stack (per goroutine!) and a heap and some things go on the stack and some on the heap. In some cases the compiler follows rigid rules (like "new always allocates on the heap") and in others the compiler does "escape analysis" to decide if an object can live on the stack or if it must be allocated on the heap.

    In your example 2, escape analysis would show the pointer to the struct escaping and so the compiler would have to allocate the struct. I think the current implementation of Go follows a rigid rule in this case however, which is that if the address is taken of any part of a struct, the struct goes on the heap.

    For question 3, we risk getting confused about terminology. Everything in Go is passed by value, there is no pass by reference. Here you are returning a pointer value. What's the point of pointers? Consider the following modification of your example:

    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
    }
    

    I modified myFunction2 to return the struct rather than the address of the struct. Compare the assembly output of myFunction1 and myFunction2 now,

    --- 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     ,
    

    Don't worry that myFunction1 output here is different than in peterSO's (excellent) answer. We're obviously running different compilers. Otherwise, see that I modfied myFunction2 to return myStructType rather than *myStructType. The call to runtime.new is gone, which in some cases would be a good thing. Hold on though, here's 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     ,
    

    Still no call to runtime.new, and yes it really works to return an 8MB object by value. It works, but you usually wouldn't want to. The point of a pointer here would be to avoid pushing around 8MB objects.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(3条)

报告相同问题?

悬赏问题

  • ¥15 python的qt5界面
  • ¥15 无线电能传输系统MATLAB仿真问题
  • ¥50 如何用脚本实现输入法的热键设置
  • ¥20 我想使用一些网络协议或者部分协议也行,主要想实现类似于traceroute的一定步长内的路由拓扑功能
  • ¥30 深度学习,前后端连接
  • ¥15 孟德尔随机化结果不一致
  • ¥15 apm2.8飞控罗盘bad health,加速度计校准失败
  • ¥15 求解O-S方程的特征值问题给出边界层布拉休斯平行流的中性曲线
  • ¥15 谁有desed数据集呀
  • ¥20 手写数字识别运行c仿真时,程序报错错误代码sim211-100