2017-02-15 85 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是它是在堆上分配(而不是限制就像你經歷過的那樣)。