2017-02-15 86 views
1

代码:Go在堆栈分配方面被认为是“小”对象?

func MaxSmallSize() { 
    a := make([]int64, 8191) 
    b := make([]int64, 8192) 
    _ = a 
    _ = b 
} 

然后运行go build -gcflags='-m' . 2>&1检查内存分配的细节。其结果是:

./mem.go:10: can inline MaxSmallSize 
./mem.go:12: make([]int64, 8192) escapes to heap 
./mem.go:11: MaxSmallSize make([]int64, 8191) does not escape 

我的问题是,为什么a小对象,b是大型对象?

make 64KB将会转移到堆中,并且少于将分配到堆中。 _MaxSmallSize = 32 << 10是原因吗?

go env

GOARCH="amd64" 
GOBIN="" 
GOEXE="" 
GOHOSTARCH="amd64" 
GOHOSTOS="linux" 
GOOS="linux" 
GOPATH="/vagrant/gopath" 
GORACE="" 
GOROOT="/home/vagrant/go" 
GOTOOLDIR="/home/vagrant/go/pkg/tool/linux_amd64" 
CC="gcc" 
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build201775001=/tmp/go-build" 
CXX="g++" 
CGO_ENABLED="1" 
+4

这是依赖于实现的,不同的编译器可能会以不同的方式执行,不同的体系结构可能会以不同的方式执行,不同的版本可能会以不同的方式处理它。那么你的问题到底是什么? – Volker

回答

1

由于该语言中的规范没有提到,它是一个实现细节,并且因此,它可以基于一些事情(转到版本,目标OS,建筑等)。

如果您想了解当前值或开始挖掘的位置,请查看cmd/compile/internal/gc包。

决定在哪里分配变量的escape analysiscmd/compile/internal/gc/esc.go。化妆片操作的检查中未导出的函数esc()

func esc(e *EscState, n *Node, up *Node) { 
    // ... 

    // Big stuff escapes unconditionally 
    // "Big" conditions that were scattered around in walk have been gathered here 
    if n.Esc != EscHeap && n.Type != nil && 
     (n.Type.Width > MaxStackVarSize || 
      (n.Op == ONEW || n.Op == OPTRLIT) && n.Type.Elem().Width >= 1<<16 || 
      n.Op == OMAKESLICE && !isSmallMakeSlice(n)) { 
     if Debug['m'] > 2 { 
      Warnl(n.Lineno, "%v is too large for stack", n) 
     } 
     n.Esc = EscHeap 
     addrescapes(n) 
     escassignSinkNilWhy(e, n, n, "too large for stack") // TODO category: tooLarge 
    } 

    // ... 
} 

涉及大小的决定是在功能上isSmallMakeSlice(),这是文件cmd/compile/internal/gc/walk.go

func isSmallMakeSlice(n *Node) bool { 
    if n.Op != OMAKESLICE { 
     return false 
    } 
    l := n.Left 
    r := n.Right 
    if r == nil { 
     r = l 
    } 
    t := n.Type 

    return Smallintconst(l) && Smallintconst(r) && (t.Elem().Width == 0 || r.Int64() < (1<<16)/t.Elem().Width) 
} 

的大小限制是这样的:

r.Int64() < (1<<16)/t.Elem().Width 

r是切片的长度或容量(如果提供了盖),t.Elem().Width是元素类型的字节大小:

NumElem < 65536/SizeElem 

你的情况:

NumElem < 65536/8 = 8192 

因此,如果切片类型为[]uint64,8192是它是在堆上分配(而不是限制就像你经历过的那样)。